AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Tableau Assessment

skill-twells89-sigma-migration-skills-tableau-assessment · by twells89

Take inventory of a Tableau Cloud site and produce a migration-readiness readout — environment counts, licenses, datasource mix, refresh history, per-workbook usage, per-workbook complexity (via .twb gap-scan), and a value/cost-ranked migration shortlist. Use when a user wants to scope a Tableau→Sigma migration, audit BI sprawl, or pick which workbooks to convert first. Lightweight (~90s) MCP-dri…

No reviews yet
0 installs
31 views
0.0% view→install

Install

$ agentstack add skill-twells89-sigma-migration-skills-tableau-assessment

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-twells89-sigma-migration-skills-tableau-assessment)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Tableau Assessment? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Tableau Assessment

Surveys a Tableau Cloud site via the Tableau Admin Insights project (MCP) and the workbook-content REST endpoint (PAT) — or a self-hosted Tableau Server site via REST only (inventory-rest.rb; no Admin Insights, so no usage/refresh stats). Emits a markdown readout + JSON inventory the user can hand to a Sigma rep, a Hakkoda engagement, or directly to the tableau-to-sigma skill for conversion of the shortlisted workbooks.

> Warehouse-agnostic. This skill (and the downstream tableau-to-sigma > conversion skill) makes no assumption about which warehouse Sigma is reading > from — BigQuery, Databricks, Snowflake, Postgres, SQL Server, Redshift, > Synapse, and Oracle are all treated the same way at the Sigma API layer > (connections → tables → columns → query). Worked examples in this skill use > Snowflake because that's where the dev / audit fixtures live, but the > Sigma-side patterns transfer to any supported warehouse. The only > warehouse-specific surface is the optional --snowflake-conn reconciliation > flag on migration-plan.rb (see "Multi-warehouse considerations" below for > the equivalent on other warehouses).


Privacy posture (READ FIRST, surface to the customer)

This skill reads workbook metadata, not warehouse data. What crosses Anthropic's API on its way through Claude:

| Crosses Anthropic API | Stays local | |---|---| | Aggregate counts (workbook count, user count, datasource counts) | View CSVs (this skill never fetches them) | | Workbook names, owner emails, project names | Warehouse rows (this skill never queries them) | | User License Type and login dates from Admin Insights | Customer database credentials | | Refresh job results, durations, error messages | The customer's actual reports' values | | .twb XML for each workbook (calc-field definitions, custom SQL, layout) | .hyper extract data files (skipped on download) |

This is a weaker posture than Hakkoda's "stays in Snowflake" app. Hakkoda's Snowflake Native App keeps everything inside the customer's Snowflake account. This skill — like every other Claude Code skill — sends what it reads through the Anthropic API to Claude. The user should be told this before running.

The skill writes outputs to a local directory (/tmp/assessment-/ by default) and does NOT upload them anywhere. If the customer wants the readout shared with a Sigma rep, that's a deliberate Share action, not automatic.

See PRIVACY.md for the full disclosure to share with customer privacy/legal review.


When to use this skill

  • A Tableau customer wants a 5-minute scoping view before booking a Hakkoda 1-hour assessment
  • A Sigma SE preparing for a discovery call wants a pre-built migration shortlist
  • A customer is deciding which Tableau workbooks to retire vs. migrate
  • A tableau-to-sigma invocation needs a Phase 0 inventory of the source site

Not for: Replacing Hakkoda's full Assessment App readout (pricing scenarios, permissions audit, dataset similarity at depth). Those still live in Hakkoda.


Scripts overview

| Script | Purpose | |---|---| | scripts/setup-tableau.sh | Symlink to the tableau-to-sigma PAT setup wizard | | scripts/get-tableau-token.sh | Symlink to the tableau-to-sigma token-refresh wrapper | | scripts/probe-admin-insights.rb | Confirm the Admin Insights project is visible (gates whether license/refresh/usage sections run) | | scripts/inventory-rest.rb | Tableau Server fallback for Phases 1–2: build inventory.json (mode: rest-server, counts + per-workbook sheet_count, usage_available: false) from the plain REST workbooks/views/datasources endpoints. No usage/refresh (needs Admin Insights or the Server repository DB). | | scripts/fetch-all-twbs.rb | Parallel download of all workbook .twb files via REST (PAT mode only) | | scripts/aggregate-complexity.rb | Run scan-workbook-gaps.rb (from tableau-to-sigma) against every .twb; emit complexity.json. Also derives a predicted-parity % + A–D band per workbook (y9rd.6): an occurrence-weighted roll-up of the gap-scanner's status tiers (auto=1.0, hint=0.85, manual=0.5, unhandled=0.0) — a pre-migration prediction from static .twb signal, NOT the post-migration measured value-parity score (verify-parity.rb). Flags hard workbooks (band C/D) before any conversion. | | scripts/build-shortlist.rb | Cross-tabulate usage × complexity; rank by value / (1 + cost); emit shortlist.json (carries predicted_parity_pct + parity_band) | | scripts/render-readout.rb | Compose final readout.md from inventory.json + complexity.json + shortlist.json | | scripts/migration-plan.rb | Phase 6: combine shortlist + data-sources + .twb warehouse-table extraction into migration-plan.json with per-workbook recommended_path (tableau-to-sigma / vds-to-snowflake / retire / blocked), DM clusters (Jaccard ≥ 0.5 on shared warehouse tables + fact-table overlap), and a suggested first batch. Input contract for the conversion handoff. | | scripts/consolidation-candidates.rb | Phase 6b: within each shared-datasource pool, score workbook-variant similarity (actually-used fields, sheet/zone structure, filter sets, gap-scan feature profile, name-stem heuristics) and emit consolidation-candidates.json — groups of workbooks that could collapse into ONE Sigma workbook + a control. --decide records the user's per-group choice into migration-plan.json. Pure analysis; no Tableau/Sigma writes. | | scripts/orchestrate-batch.rb | Phase 7 (optional): produce a batch-plan.json with wave-style scheduling for parallel tableau-to-sigma subagent execution. Cluster leaders run first to build/pick their DM; followers reuse via find-or-pick-dm.rb + inspect-dm-shape.rb. Continue-on-failure. Outputs ready-to-fire agent_brief strings for the conversation-layer to pass into Agent() calls. |

Scripts that need warehouse-table data (the MCP query-datasource calls against Admin Insights) are NOT scripts — the agent fires those directly per the recipes in this SKILL.md, because MCP tool calls only work from the agent's context.


Modes

| Mode | Setup | Coverage | Use when | |---|---|---|---| | MCP-only | None — just Tableau MCP loaded as Site Admin | Environment + Licenses + Datasource mix + Refresh + Usage | Quick pre-scope; customer hasn't issued a PAT | | MCP + PAT (recommended) | ruby scripts/setup-tableau.sh once (~30s) | Adds per-workbook complexity scan + ranked migration shortlist | Real migration planning; full readout |

The user driving the skill MUST have Site Admin role in Tableau — Admin Insights is only published to that group by default. The skill probes this and surfaces a clear error if Admin Insights isn't visible.


Phase 0 — Probe access

Confirm the user has the access the skill needs. Two checks, in order:

# 0a. Tableau MCP loaded? Try a cheap call:
mcp__tableau__list-workbooks   limit=1
# If "tool not found" → MCP isn't loaded. Skill cannot run.
# If 401/403 → user signed in but lacks site-level read access.

# 0b. Admin Insights visible?
ruby scripts/probe-admin-insights.rb
# Calls mcp__tableau__search-content for "Admin Insights"; reports which of the
# 10 expected datasources are reachable. Exits 1 if zero are reachable (user is
# not a Site Admin → can run only the Section 1 inventory below).

If only the basic inventory runs (Section 1 below), surface a banner in the readout:

> "Run this as a Tableau Site Admin to unlock license, refresh, and usage > sections. Currently running with limited access."


Scope filters — usage window + personal sandbox exclusion

Every TS Events / Site Content query in this skill applies two default filters so the readout reflects current relevance, not lifetime noise:

| Filter | Default | Why | How to override | |---|---|---|---| | Usage window | Event Date >= today - 90 days on every TS Events query | Tableau Cloud's TS Events Admin Insights datasource only retains ~90 days anyway, but the filter makes the window explicit in the readout and lets you tighten it to 30 days for pilot-picking. Without an explicit Event Date filter, "all time" silently means whatever the customer's site retention is. | Set USAGE_DAYS=30 (or any positive int) in env before running the agent's Admin Insights queries; surface in the readout header as "Usage window: last N days". | | Personal Space exclusion | Top Parent Project Name != "Personal Space" on Site Content workbook/datasource queries | Tableau Cloud's per-user sandbox project is full of one-off / draft / never-shared workbooks. On a 793-dashboard site, this often hides ~30-50% of the count. | Set INCLUDE_PERSONAL=1 to keep them in the inventory. |

> There is no Is Archived field on Tableau Cloud's Admin Insights Site > Content datasource — verified against read-metadata on a live site. The > Tableau Cloud REST /workbooks endpoint already filters out truly archived > /deleted workbooks server-side, so no client filter is needed for that. The > "archived" concept the customer might mean is usually either (a) personal > sandbox content (handled by the Personal Space exclusion above) or (b) > workbooks moved to a project the customer calls "Archive" / "Old" / "Retired" > — add those project names to --exclude-projects if surfaced.

Compute the relative date in the orchestration shell, e.g. MIN_DATE=$(date -v-90d +%Y-%m-%d) (BSD/macOS) or MIN_DATE=$(date -d '90 days ago' +%Y-%m-%d) (GNU/Linux), then substitute into the QUANTITATIVE_DATE filter shape shown in the queries below.


Phase 1 — Environment inventory (MCP, always runs)

Even without Admin Insights, the skill can produce a basic environment overview from mcp__tableau__search-content and mcp__tableau__list-workbooks. This is the "even broken access still produces something" floor.

mcp__tableau__list-workbooks                                     # → workbook count + sheetCount/hasExtracts per workbook
mcp__tableau__search-content filter.contentTypes=["datasource"]  # → datasource count
mcp__tableau__list-views                                         # → view count

Write the rolled-up counts to inventory.json's environment_overview key.

Self-hosted Tableau Server (no MCP / no Admin Insights)

Tableau Server has no Admin Insights project (that's a Cloud feature) and the hosted Tableau MCP targets Cloud, so Phases 1–2 above don't apply. Instead build the inventory from the standard REST endpoints with a PAT:

eval "$(scripts/get-tableau-token.sh)"            # negotiates the Server's REST API version
ruby scripts/inventory-rest.rb --out /tmp/assessment-

This writes inventory.json (mode: "rest-server") with workbook/datasource/view counts, a per-workbook sheet_count, and usage_available: false. Usage, license, and refresh history are NOT available over plain REST — they require Admin Insights (Cloud) or the Tableau Server repository (the "workgroup" Postgres DB an admin must enable). Phase 3 complexity (fetch-all-twbs.rb + scan-workbook-gaps.rb) is already REST/PAT and runs unchanged. build-shortlist.rb detects usage_available: false and ranks by complexity (easiest first) — it will not tag zero-usage workbooks retire, since on Server a missing access count is not evidence of disuse.


Phase 2 — Admin Insights queries (MCP, requires Site Admin)

For each of the queries below, call mcp__tableau__query-datasource with the listed datasourceLuid and query payload. Run queries sequentially, not in parallel — VizQL session contention causes 401s under fan-out.

Field names matter — Admin Insights field naming is inconsistent and a typo silently fails. See refs/admin-insights-fields.md for the verified field-name cheat sheet. Critical: it's Event Id, not Event LUID.

2a. Site-content item counts → inventory.environment_overview

{
  "datasourceLuid": "",
  "query": { "fields": [
    { "fieldCaption": "Item Type" },
    { "fieldCaption": "Item LUID", "function": "COUNTD", "fieldAlias": "n" }
  ]}
}

2b. License breakdown → inventory.licenses

{
  "datasourceLuid": "",
  "query": { "fields": [
    { "fieldCaption": "User License Type" },
    { "fieldCaption": "User Site Role" },
    { "fieldCaption": "User LUID", "function": "COUNTD", "fieldAlias": "users" },
    { "fieldCaption": "Days Since Last Login", "function": "AVG", "fieldAlias": "avg_days_since_login" }
  ]}
}

2c. Content ownership → inventory.content_ownership

{
  "datasourceLuid": "",
  "query": { "fields": [
    { "fieldCaption": "Item Type" },
    { "fieldCaption": "Owner Email" },
    { "fieldCaption": "Item LUID", "function": "COUNTD", "fieldAlias": "n" }
  ], "filters": [{
    "field": { "fieldCaption": "Item Type" },
    "filterType": "SET",
    "values": ["Workbook", "Datasource", "Flow", "View"]
  }]}
}

2d. Datasource types + extract mix → inventory.datasource_types

{
  "datasourceLuid": "",
  "query": { "fields": [
    { "fieldCaption": "Data Source Content Type" },
    { "fieldCaption": "Data Source Database Type" },
    { "fieldCaption": "Is Data Extract" },
    { "fieldCaption": "Item LUID", "function": "COUNTD", "fieldAlias": "n" }
  ], "filters": [{
    "field": { "fieldCaption": "Item Type" }, "filterType": "SET", "values": ["Datasource"]
  }]}
}

2e. Refresh history → inventory.refresh_jobs

{
  "datasourceLuid": "",
  "query": { "fields": [
    { "fieldCaption": "Job Type" },
    { "fieldCaption": "Final Job Result" },
    { "fieldCaption": "Job ID", "function": "COUNTD", "fieldAlias": "jobs" },
    { "fieldCaption": "Job Duration", "function": "AVG", "fieldAlias": "avg_duration_s" }
  ]}
}

2f. Workbook usage ranking → inventory.workbook_usage

{
  "datasourceLuid": "",
  "query": { "fields": [
    { "fieldCaption": "Workbook Name" },
    { "fieldCaption": "Number of Events", "function": "SUM", "fieldAlias": "accesses", "sortDirection": "DESC", "sortPriority": 1 },
    { "fieldCaption": "Count of Distinct Actors", "fieldAlias": "actors" }
  ], "filters": [
    { "field": { "fieldCaption": "Event Type" }, "filterType": "SET", "values": ["Access"] },
    { "field": { "fieldCaption": "Item Type" }, "filterType": "SET", "values": ["View", "Workbook"] },
    { "field": { "fieldCaption": "Event Date" }, "filterType": "QUANTITATIVE_DATE", "quantitativeFilterType": "MIN", "minDate": "" }
  ]}
}

2g. Workbook inventory (size, owner, last accessed, hyperlink) → inventory.workbook_inventory

{
  "datasourceLuid": "",
  "query": { "fields": [
    { "fieldCaption": "Item Name" },
    { "fieldCaption": "Owner Email" },
    { "fieldCaption": "Top Parent Project Name" },
    { "fieldCaption": "Size (MB)", "function": "SUM", "fieldAlias": "size_mb" },
    { "fieldCaption": "Last Accessed At" },
    { "fieldCaption": "Is Data Extract" },
    { "fieldCaption": "Has Refresh Scheduled" },
    { "fieldCaption": "Item Hyperlink" }
  ], "filters": [
    { "field": { "fieldCaption": "Item Type" }, "filterType": "SET", "values": ["Workbook"] },
    { "field": { "fieldCaption": "Top Parent Project Name" }, "filterType": "SET", "exclude": true, "values": ["Personal Space"] }
  ]}
}

> Drop the Top Parent Project Name exclusion filter if INCLUDE_PERSONAL=1. > Add additional project names to the values array if the customer has > custom "Archive" / "Retired" projects to skip.

Merge the seven outputs into /inventory.json following the schema in refs/output-shapes.md.

2h. Per-user usage map → users.json (after running analyze-users.rb)

For user-population segmentation and per-user migration coverage:

{
  "datasourceLuid": "",
  "query": { "fields": [
    { "fieldCaption": "User Email" },
    { "fieldCaption": "User License Type" },
    { "fieldC

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [twells89](https://github.com/twells89)
- **Source:** [twells89/sigma-migration-skills](https://github.com/twells89/sigma-migration-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.