Run your Tableau Cloud site from a chat box
Promote a workbook from Dev to UAT. Audit a user's permissions. Archive everything an offboarded employee owns. Three real admin scenarios. All done by asking an AI agent in plain English — backed by a Model Context Protocol server that exposes 19 narrow, well-named tools to the agent.
Antoine just showed you the three pillars — Context, Structure, Verification — applied to writing a Tableau viz extension. Same playbook here, pointed at the admin side: Context = a site with users/groups/projects already wired; Structure = 19 narrow tools the agent picks from instead of free-handing REST calls; Verification = each tool returns a structured envelope so you see exactly what changed.
Chapter 1Your setup — 5 min
Three things to wire up: a Salesforce org for Vibes, the MCP server URL, and one verification prompt. Everything runs in the browser.
| What | Where |
|---|---|
| Agentforce Vibes | Browser-based AI coding tool — no local install |
| Tableau Cloud site | Shared site datamanagetab, pre-populated with demo content |
| MCP server | Hosted on Heroku — already deployed, just paste the URL into Vibes |
Strongly recommended: claim a fresh Salesforce org for this lab (Step 0 below). It guarantees a clean slate and ensures every Vibes setting we tweak doesn't conflict with whatever you did in Antoine's session. If you really want to reuse the org from the previous workshop you can — your Vibes is already open and you can skip to Step 2 — but be aware leftover state from Antoine's lab can occasionally trip the agent.
Salesforce blocks several corporate VPNs. If you try to claim or open an org with a VPN on, the org gets locked and becomes unusable for the rest of the session. Turn your VPN off now, then come back.
0 Claim a Salesforce org for Vibes
Vibes runs inside a Salesforce developer org. The org farm hands them out instantly.
- Go to
orgfarm.salesforce.com/signup - Claim an org with the code
5GPWZRJ3 - Open the org once it's ready (about 30 seconds)
1 Open Agentforce Vibes
Vibes is a browser-based AI editor — same idea as Claude Code or Cursor, with nothing to install locally.
- Inside the org, click the Gear icon (top-right) and choose Open Agentforce Vibes. A new tab opens — wait a minute while the environment loads.
- You don't need to clone any repo for this lab — all the action happens in the chat panel using MCP tools, not local files. Just close the welcome screen.
Known bug: the Vibes token sometimes expires and you get kicked back to a login screen. Don't claim a new org — just sign back in:
1. Inside the Salesforce org, grab your Username (top-right menu → Settings → My Personal Information, or visible in the org URL).
2. Go to login.salesforce.com and sign in with that username + password Orgfarm1234.
3. Reopen Vibes from the gear icon. Your MCP config is still there.
2 Add the Tableau Admin MCP server
This is the bridge between Vibes and your Tableau Cloud site. The server is already running on Heroku — you just declare it.
- In Vibes, open the command palette (Cmd/Ctrl + Shift + P)
- Type “MCP: Add Server” and pick it
- Pick HTTP transport, then paste this URL when prompted:
loading…
- Name the server
tableau-cloud-admin - Vibes should refresh and show 19 tools available from this server (open the MCP panel to confirm)
All 30 attendees share one Tableau Cloud site (datamanagetab) and one MCP server, but each of you has a dedicated sandbox project (40 are pre-created: sandbox-user01 through sandbox-user40). Use the picker in the blue banner above to set your table number — every prompt below will be re-written to target your sandbox, and your choice is remembered.
3 Configure Vibes for fast iteration
Two settings cut about 60% of the friction during the lab.
- In the chat panel header, switch to Act Mode (Cmd/Ctrl + Shift + A). Plan Mode is great for thinking; Act Mode is what you want for executing.
- For each MCP tool that pops up the “wants to use a tool” confirmation, click Auto-approve. Toggle it on for at least
list_workbooks,list_users,list_user_content,get_workbook_permissions(the read tools) — write tools are your call.
4 Verify — say hi to the site
One prompt, one tool call, one confirmation that everything is wired.
Show me my Tableau Cloud site info — what site I'm signed into and as who.
whoami
Site datamanagetab, signed in as jordan-nguyen-01@outlook.fr (the shared service account). If the agent returns ok: true with these fields, you're live.
Exercise 1Promote a workbook from Dev to UAT
The most common admin task: a developer finishes a draft, you move it to UAT, rename it, and give the QA group viewer rights. Three REST endpoints in the UI — one prompt here.
Your sandbox sandbox-user01 already has Dev/Sales Q4 - Draft owned by Alice. You'll promote it to UAT inside your sandbox (so 30 attendees can run this in parallel without colliding).
Promote the Sales Q4 - Draft workbook from {{SANDBOX}}/Dev to {{SANDBOX}}/UAT — rename it to drop the - Draft suffix and grant the Sales-Viewers group viewer permissions on the promoted version. Pass source_project_parent='{{SANDBOX}}' and dest_project_parent='{{SANDBOX}}' on move_workbook so you target my sandbox unambiguously.
list_projectsmove_workbookgrant_workbook_permission
1. (Optionally) Call list_projects to confirm your sandbox's Dev/UAT project IDs.
2. Call move_workbook with workbook_name="Sales Q4 - Draft", source_project="Dev", source_project_parent="sandbox-user01", dest_project="UAT", dest_project_parent="sandbox-user01", new_name="Sales Q4". The agent must pass both source_project_parent and dest_project_parent — without them, the MCP returns an ambiguity error because Dev and UAT both exist under every sandbox-userNN.
3. Call grant_workbook_permission with grantee_group="Sales-Viewers", preset="viewer".
4. Return a chained summary of what changed.
Try chaining further: ask “and then show me the permissions you just set”. The agent will call get_workbook_permissions as a verification step. This is the Verification pillar in action — you don't trust, you check.
Exercise 2Audit a user's permissions
A security exercise: an internal audit asks “what does Alice have access to?”. You answer in 30 seconds instead of 30 minutes of clicking.
Show me everything alice owns inside my sandbox (use list_user_content with user_email='alice' and project_parent='{{SANDBOX}}'). Then for each workbook returned, list the permissions granted to other users and groups.
list_user_content(project_parent)get_workbook_permissions
You typed alice. The MCP fuzzy-resolves to jordan-nguyen-01+alice@outlook.fr automatically (it searches email + fullname, must be unique — if ambiguous, it returns the candidates so the agent can disambiguate). No more copy-pasting long emails. Same for dave below.
For round two, swap the role: grant the auditor temporary access (scoped to your sandbox).
Grant carol the viewer preset on every workbook alice owns in my sandbox (re-use list_user_content with project_parent='{{SANDBOX}}' to scope the listing). Show me the result.
list_user_contentgrant_workbook_permission
Carol has the Viewer site role on purpose — she can be granted workbook permissions, but she can't own a workbook. If you try change_workbook_owner to carol the tool will return a clear hint explaining the site_role constraint.
Exercise 3Offboard a user (the most common one nobody automates)
Dave just left the company. You need to: inventory what he owned, move it to an archive project, reassign ownership to someone still around, then deactivate his account. The UI is 6 different screens. The agent makes it three prompts — and one of them is just a dry-run preview.
Dave just left the company. Show me everything he owns on the site so I can plan the cleanup.
list_user_content
Important — scope it to your sandbox. Dave owns content in every sandbox (one set per attendee). If you ask "show me everything Dave owns" without qualifying, you'll get 80+ items — every other attendee's copy. Always include your sandbox name as a filter.
Dave just left the company. Show me everything he owns inside my sandbox {{SANDBOX}}. Use list_user_content with user_email='dave' and project_parent='{{SANDBOX}}' to scope the listing to my sandbox only.
list_user_content(project_parent)
Preview what would happen if I archived all of dave's content from my sandbox to the {{SANDBOX}}/Archive project. Use archive_project='Archive', archive_project_parent='{{SANDBOX}}', source_project_parent='{{SANDBOX}}', dry_run=true.
archive_user_content(dry_run=true, source_project_parent)
A table of every workbook and datasource Dave owns in your sandbox (2 workbooks: Marketing KPIs - WIP and Customer 360 - Draft) with the proposed new names (suffixed with -archived-<HHMMSSXX> to avoid collisions in your Archive project) and the destination. No writes happen yet.
Looks good. Actually archive it now (same scope: source_project_parent='{{SANDBOX}}', archive_project='Archive', archive_project_parent='{{SANDBOX}}') and transfer ownership of every moved item to alice with new_owner_email='alice'.
archive_user_content(dry_run=false, new_owner_email)
Re-run prompt 1 ("Show me everything dave owns in my sandbox"). Dave should now own zero items in your sandbox. The agent will show you the empty list — that's your verification.
BonusIf you finish early
All of these are one-prompt exercises against the same site state. Pick anything that looks fun.
Copy permissions between workbooks
First do Exercise 1 so you have a UAT/Sales Q4 in your sandbox. Then:
Copy all the permissions from {{SANDBOX}}/UAT/Sales Q4 onto {{SANDBOX}}/Dev/Customer 360 - Draft, clearing the existing rules first. Pass template_project_parent='{{SANDBOX}}' and target_project_parent='{{SANDBOX}}' to scope to my sandbox.
set_workbook_permissions_from_templateCompare Dev vs UAT
Compare the workbooks between {{SANDBOX}}/Dev and {{SANDBOX}}/UAT. Pass project_a_parent='{{SANDBOX}}' and project_b_parent='{{SANDBOX}}'. Show me what's in one but not the other.
compare_workbooks_in_projectsClone a draft for a hotfix
Clone {{SANDBOX}}/Dev/Marketing KPIs - WIP into {{SANDBOX}}/UAT as a snapshot named Marketing KPIs - Hotfix Review. Pass source_project_parent='{{SANDBOX}}' and dest_project_parent='{{SANDBOX}}'. Keep me as the owner.
clone_workbookGroup inventory
List every group on the site and who's in it.
list_groups_with_membersDatasource inventory & connections
List every published datasource on the site, then for the largest one, show me its connections (server, port, user).
list_datasourceslist_connectionsThis is the prompt you'd use in production after rotating a DB password: "The Sales PG database password just rotated. Update the credentials on the Sales-Q4-Live datasource's connection — new user is sales_reader_v2, new password is <paste>, and keep it embedded." The agent will list_connections first to confirm the target, then update_connection_credentials in one step. The demo site doesn't have a live DB datasource, so the rotation will error out cleanly — the structure is what matters.
Appendix AAll 19 tools
The agent picks from this set. You don't need to call them by name — natural language works because the docstrings tell the agent what each one does.
Read · explore the site
| Tool | Does what |
|---|---|
whoami | Show signed-in user + site |
list_workbooks | List workbooks, filtered by project or owner (fuzzy) |
list_datasources | List published datasources, filtered by project or owner |
list_connections | Connections on a workbook OR datasource (server, port, user, embedded) |
list_projects | Project tree (hides system Default) |
list_users | Users on the site, filtered by role or email substring |
list_groups_with_members | Groups + members (hides All Users) |
list_user_content | Inventory everything a user owns (workbooks + datasources) |
get_workbook_permissions | ACLs on a workbook (per user, per group) |
compare_workbooks_in_projects | Diff two project's workbook lists |
Write · change the site
| Tool | Does what |
|---|---|
move_workbook | Change a workbook's project (with auto-rename) |
rename_workbook | Rename in place |
clone_workbook | Download + republish as a new workbook |
grant_workbook_permission | Grant a preset (viewer/explorer/editor) or explicit capabilities |
revoke_workbook_permission | Remove specific capabilities or all |
set_workbook_permissions_from_template | Copy all ACLs from one workbook to another |
change_workbook_owner | Transfer ownership (minimal REST PUT — dodges TSC's name-update validation bug) |
archive_user_content | Soft-delete: move everything a user owns to an archive project, optionally reassign |
update_connection_credentials | Rotate DB user/password on a workbook OR datasource connection |
All tools accept fuzzy identifiers — "alice", "dave", "sales" are resolved to a unique user/group/email. Ambiguous inputs return a list of candidates so the agent can disambiguate.
Appendix BReference
| Resource | Link |
|---|---|
| This MCP server | loading… |
| Tableau Cloud site | loading… |
| Tableau REST API docs | help.tableau.com/.../REST |
| Connected App + JWT auth | help.tableau.com/.../connected_apps |
| Model Context Protocol | modelcontextprotocol.io |
| FastMCP (Python) | github.com/modelcontextprotocol/python-sdk |
| Tableau Server Client (TSC) | tableau.github.io/server-client-python |