Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/docs/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,4 @@ Prompts orchestrate multiple tools into a guided admin workflow. They are gated
| [job-optimization-inform](prompts/job-optimization-inform.md) | Admin-only. Read-only. Analyzes Admin Insights job performance and surfaces optimization signals. |
| [extract-optimization-apply](prompts/extract-optimization-apply.md) | Admin-only. Destructive. Applies schedule downgrades and deletions to extract refresh tasks after a required human-confirmation break; defaults to a dry-run report. |
| [user-license-reclamation-inform](prompts/user-license-reclamation-inform.md) | Admin-only. Read-only. Identifies inactive licensed users who are candidates for downgrade to Unlicensed by cross-referencing `list-users` with TS Events activity. |
| [user-license-reclamation-apply](prompts/user-license-reclamation-apply.md) | Admin-only. Destructive. Identifies inactive licensed users, surfaces owned-content counts, and — after a required human-confirmation break — downgrades approved users to Unlicensed; defaults to a dry-run report. |
56 changes: 56 additions & 0 deletions docs/docs/prompts/user-license-reclamation-apply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
sidebar_position: 5
---

# User License Reclamation — Apply

`user-license-reclamation-apply`

A guided, **destructive** Tableau Cloud admin workflow that identifies inactive licensed users, surfaces their owned-content counts for review, and — only after explicit human approval — downgrades approved users to **Unlicensed** via `update-user`.

:::warning[Admin Only · Destructive]
This prompt is restricted to Tableau site administrators and requires the `ADMIN_TOOLS_ENABLED` feature flag. It drives the destructive [`update-user`](../tools/users/update-user.md) tool. The user inventory, activity analysis, and ownership inventory steps are **read-only**: no user is downgraded until the admin approves a specific user set at the required human-in-the-loop confirmation break.
:::

## Workflow

The prompt sequences existing deterministic tools — it performs no calculations itself. Steps 1–3 are read-only; no write happens until after the Step 4 approval break:

1. **User inventory (read-only)** — calls [`list-users`](../tools/users/list-users.md) to retrieve all users on the site, then filters client-side to licensed roles in scope. Users with null `lastLogin` (never signed in) are also included as candidates.
2. **Activity signals (read-only)** — calls [`query-admin-insights`](../tools/admin-insights/query-admin-insights.md) with `kind: "ts-events"` to retrieve recent Access events. Cross-references activity against candidates from Step 1 to identify truly inactive users. TS Events lookback is capped at 90 days on standard Tableau Cloud (365 with Advanced Management); data is subject to 24–48h ETL lag.
3. **Ownership inventory (read-only)** — calls [`query-admin-insights`](../tools/admin-insights/query-admin-insights.md) with `kind: "site-content"` to count workbooks and data sources owned by each inactive user (matched by `Owner Email`). This is informational only — ownership is **not** affected by the downgrade.
4. **Human confirmation break** — presents the inactive users as a table (username, display name, current role, last login, days inactive, owned workbooks, owned datasources) and requires explicit approval before any downgrade. In a dry run (the default) the workflow stops here.
5. **Apply (only after Step 4 approval)** — for each approved user, calls [`update-user`](../tools/users/update-user.md) with `siteRole: "Unlicensed"`. Calls are sequential; the first error stops the run.
6. **Final report** — prints a "Changes applied" section, a "Skipped" section, and an "Ownership reminder" noting that downgraded users' content remains intact and can be reassigned separately.

## Arguments

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `inactiveDays` | string (integer) | No | Minimum days since last login for a user to be considered inactive. Defaults to 90. Clamped to 1–3650. Bounded by TS Events 90-day lookback window unless Advanced Management is enabled. |
| `siteRoles` | string | No | Comma-separated list of site roles to scope reclamation to (e.g. "Viewer, Explorer"). Defaults to all license-consuming roles: Creator, Explorer, ExplorerCanPublish, SiteAdministratorCreator, SiteAdministratorExplorer, Viewer. |
| `userIds` | string | No | Comma-separated user LUIDs to scope the reclamation to. When omitted, all inactive users matching the criteria are analyzed. |
| `dryRun` | `"true"` \| `"false"` | No | When `true` (default), produces only the reclamation report — never calls `update-user`. Set to `false` to allow the apply step after the confirmation break. |

## Safety guarantees

- No user is downgraded until the admin approves a specific user set at the Step 4 break.
- The workflow only downgrades users the admin explicitly approved; unapproved users are never touched.
- Downgrading to Unlicensed does **not** delete or reassign content — ownership is retained.
- `update-user` is reversible by re-assigning the user's prior site role.
- Apply calls run sequentially; the first error stops the run so the admin can review partial state.
- TS Events lookback is 90 days on standard Tableau Cloud. Data is subject to 24–48h ETL lag — candidates are provisional, not definitive.

## Configuration

```bash
Comment thread
Akash-Rastogi marked this conversation as resolved.
ADMIN_TOOLS_ENABLED=true

# Optional — override defaults for both inform and apply prompts:
LICENSE_RECLAIM_INACTIVE_DAYS=90 # 1–3650; default 90
LICENSE_RECLAIM_ROLES=Creator,Explorer,ExplorerCanPublish,SiteAdministratorCreator,SiteAdministratorExplorer,Viewer
```

Note: The apply prompt's default roles include all six license-consuming roles (including site-admin compound variants). The inform prompt defaults to a narrower set (`Creator,Explorer`). When `LICENSE_RECLAIM_ROLES` is set, both prompts use the configured value.

See also: [Environment Variables](../configuration/mcp-config/env-vars.md)
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@tableau/mcp-server",
"description": "Helping agents see and understand data.",
"version": "3.4.0",
"version": "3.5.0",
Comment thread
Akash-Rastogi marked this conversation as resolved.
"repository": {
"type": "git",
"url": "git+https://github.com/tableau/tableau-mcp.git"
Expand Down
2 changes: 2 additions & 0 deletions src/prompts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getJobOptimizationInformPrompt } from './jobOptimization/inform.js';
import { WebPromptFactory } from './registry.js';
import { getStaleContentCleanupApplyPrompt } from './staleContent/apply.js';
import { getStaleContentCleanupInformPrompt } from './staleContent/inform.js';
import { getUserLicenseReclamationApplyPrompt } from './userLicenseReclamation/apply.js';
import { getUserLicenseReclamationInformPrompt } from './userLicenseReclamation/inform.js';

const webPromptFactories: ReadonlyArray<WebPromptFactory> = [
Expand All @@ -13,6 +14,7 @@ const webPromptFactories: ReadonlyArray<WebPromptFactory> = [
getJobOptimizationInformPrompt,
getExtractOptimizationApplyPrompt,
getUserLicenseReclamationInformPrompt,
getUserLicenseReclamationApplyPrompt,
];

export const registerPrompts = (server: WebMcpServer): void => {
Expand Down
Loading
Loading