Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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