tableau-desktop-mcp
A local MCP server that allows LLMs to inspect and safely edit Tableau Desktop workbooks (.twb) via XML, including creating native calculated fields using Tableau formula syntax.
README
tableau-desktop-mcp
Local MCP (Model Context Protocol) server that lets Claude Desktop, Claude
Code, or Codex inspect and safely edit Tableau Desktop workbooks (.twb)
by reading and rewriting their XML. The flagship feature is creating native
Tableau calculated fields — conceptually similar to authoring DAX measures in
Power BI, but using Tableau formula syntax (never DAX).
Example prompt: "Analise o workbook dashboard_vendas.twb e crie um campo calculado chamado Ticket Médio usando Receita e Pedido ID."
1. Objective
Give an LLM a small, safe toolbox to:
- locate workbooks in an authorized folder;
- analyze their datasources and fields;
- verify real column names;
- generate correct Tableau formulas;
- create an automatic backup;
- insert/update/delete calculated fields in the
.twb; - validate the XML before and after writing;
- return a structured change report.
2. Architecture
src/tableau_mcp/
├── server.py FastMCP server (stdio) — registers the 12 tools
├── config.py Centralized settings (.env), no hard-coded paths
├── models.py Pydantic models for every input/response/metadata
├── exceptions.py Typed exception hierarchy
├── logging_config.py Structured JSON logging (stderr + rotating file)
│
├── workbook/ File + XML layer (I/O)
│ ├── xml_utils.py Safe path resolution + hardened lxml parse/serialize
│ ├── reader.py Listing + read-only loading
│ ├── inspector.py Tree → typed metadata (datasources/fields/…)
│ ├── validator.py XML well-formedness / structure checks
│ ├── backup.py Timestamped, unique-id backups
│ └── writer.py Atomic, backup-protected write flow
│
├── calculations/ Formula layer (pure, no I/O)
│ ├── dax_detector.py Detect Power BI / DAX syntax
│ ├── field_reference_parser.py Parse & match [Field] references
│ ├── validator.py Rule-based formula validation
│ ├── element_builder.py Build/locate <column> calc elements (lxml)
│ └── formula_templates.py Known functions + reusable templates
│
└── tools/ Thin MCP tool functions
├── context.py Dependency container (reader/backup/writer)
├── workbook_tools.py
├── field_tools.py
└── calculation_tools.py
Design choices worth noting (deviations from the original spec):
- Added
calculations/element_builder.pyandtools/context.pyto keep XML construction and dependency wiring out of the tool functions (separation of concerns). Everything the spec required still exists. server.pyis meant to run as a module (python -m tableau_mcp.server), which is more robust on Windows than invoking the file path directly. The example Claude config reflects this.- Added
pydantic-settings(needed for.env-based configuration).
Write flow (safety):
read original → validate → backup → mutate in memory → write temp file
→ validate temp XML → atomic replace → validate final file → log
If any step fails after the backup, the original is restored automatically.
3. Limitations (MVP)
.twbonly — packaged.twbxfiles are intentionally rejected.- No worksheet/dashboard/chart generation.
- No visual automation (no PyAutoGUI).
- The formula validator is rule-based, not the real Tableau compiler; it catches common problems (unbalanced brackets, DAX, unknown references, naive division-by-zero, aggregate/non-aggregate mixing) but cannot guarantee a formula compiles in Tableau.
- Tableau Desktop has no local MCP API; this server works purely on the XML.
4. Requirements
- Windows 10/11, Python 3.11+
- The Python packages in
requirements.txt/pyproject.toml(mcp,lxml,pydantic,pydantic-settings,python-dotenv).
5–10. Installation & running (Windows / VS Code)
# From the project root
powershell -ExecutionPolicy Bypass -File scripts\setup.ps1
This creates .venv, installs the project (editable) with dev tools, and
copies .env.example → .env. Or do it manually:
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install --upgrade pip
.\.venv\Scripts\python.exe -m pip install -e ".[dev]"
Copy-Item .env.example .env
Edit .env to point at your workbook/backup/log folders:
TABLEAU_MCP_WORKBOOKS_DIR=workbooks
TABLEAU_MCP_BACKUPS_DIR=backups
TABLEAU_MCP_LOGS_DIR=logs
TABLEAU_MCP_LOG_LEVEL=INFO
Put your .twb files in the workbooks folder (the server never reads or
writes outside of it). Then run the server:
powershell -ExecutionPolicy Bypass -File scripts\run_server.ps1
# or
.\.venv\Scripts\python.exe -m tableau_mcp.server
The server communicates over stdio and prints nothing to stdout; logs go to
stderr and logs\tableau_mcp.log.
11. Claude Desktop configuration
Copy claude_desktop_config.example.json into your Claude Desktop config
(%APPDATA%\Claude\claude_desktop_config.json), adjusting the paths:
{
"mcpServers": {
"tableau-desktop": {
"command": "C:\\tableau-mcp\\.venv\\Scripts\\python.exe",
"args": ["-m", "tableau_mcp.server"],
"cwd": "C:\\tableau-mcp",
"env": {
"TABLEAU_MCP_WORKBOOKS_DIR": "C:\\tableau-mcp\\workbooks",
"TABLEAU_MCP_BACKUPS_DIR": "C:\\tableau-mcp\\backups",
"TABLEAU_MCP_LOGS_DIR": "C:\\tableau-mcp\\logs"
}
}
}
}
command + args vs. a URI
This is a local stdio server, so Claude launches the process directly:
command is the Python executable and args tells it which module to run.
Claude then talks to the server over the process's stdin/stdout pipes.
A URI-based configuration (e.g. "url": "https://…/mcp") is only used for
remote MCP servers reached over HTTP/SSE — such as Tableau Cloud/Server.
This project deliberately needs no URI, URL, Cloud, or Server; everything is
local. If you ever see a config asking for a URL, that is a different,
remote integration — not this one.
12. Claude Code configuration
claude mcp add tableau-desktop -- C:\tableau-mcp\.venv\Scripts\python.exe -m tableau_mcp.server
Or add an entry to .mcp.json in your project with the same command/args
shape as the Claude Desktop example above.
13. Codex configuration (when applicable)
Codex-style clients that support local MCP servers use the same launch
contract. In the client's MCP config, register a server whose command is the
venv Python and whose args are -m tableau_mcp.server, with the
TABLEAU_MCP_* environment variables set as above. No URL is required.
14. Running the tests / quality checks
powershell -ExecutionPolicy Bypass -File scripts\run_tests.ps1 # pytest
powershell -ExecutionPolicy Bypass -File scripts\check.ps1 # ruff + mypy
The unit tests use tiny in-memory .twb fixtures and do not require Tableau
Desktop.
15. Example prompts
Liste os workbooks disponíveis.
Inspecione dashboard_vendas.twb sem realizar alterações.
Liste todos os campos calculados da fonte Vendas.
Valide a fórmula SUM([Receita]) / COUNTD([Pedido ID]).
Crie o campo Ticket Médio usando Receita e Pedido ID.
Crie os KPIs Receita Total, Pedidos, Ticket Médio e Taxa de Conversão.
16. Backup flow
Every mutating tool (create/update/delete/batch/restore) creates a
timestamped backup before touching the file, e.g.:
backups/dashboard_vendas.20260718T130501Z.a1b2c3d4.twb
You can also back up manually with backup_workbook.
17. Restore
# List available backups (no backup_name):
restore_workbook_backup(filename="dashboard_vendas.twb")
# Restore a specific one (snapshots the current state first, requires confirm):
restore_workbook_backup(filename="dashboard_vendas.twb",
backup_name="dashboard_vendas.20260718T130501Z.a1b2c3d4.twb",
confirm=true)
18. Troubleshooting
| Symptom | Likely cause / fix |
|---|---|
workbook_not_found |
File is not in the TABLEAU_MCP_WORKBOOKS_DIR folder. |
security_error |
Path traversal / wrong extension / outside the sandbox. Use just the filename. |
workbook_parse_error |
The .twb is not valid XML (or is actually a .twbx). |
duplicate_field |
A field with that caption already exists — use update_calculated_field. |
formula_validation_error |
See the validation.issues list for the exact problem. |
| Server "does nothing" in a terminal | Correct — it waits for an MCP client on stdio. Use Claude, or scripts\inspect_workbook.ps1. |
19. Risks of editing .twb files
Editing workbook XML by hand (or via automation) can, in rare cases, corrupt a workbook or produce a formula Tableau ultimately rejects. This project mitigates that with mandatory backups, atomic writes, and pre/post validation — but you should keep your own backups of important workbooks and review the change report before relying on the result.
20. ⚠️ Close Tableau Desktop first
Always close the workbook in Tableau Desktop before running any mutating tool. Tableau may hold the file open and can overwrite your changes on save, or lock the file so the write fails. Open it again in Tableau after the MCP reports success.
Suggested instruction for the assistant
Add this to your Claude/Codex system or project instructions:
Antes de criar ou alterar qualquer campo calculado:
1. inspecione o workbook;
2. confirme a datasource correta;
3. liste os campos reais;
4. valide os campos referenciados;
5. utilize somente sintaxe nativa do Tableau;
6. nunca utilize DAX;
7. valide a fórmula;
8. crie backup;
9. aplique a alteração;
10. informe exatamente o que foi modificado.
Future compatibility
The layering (workbook I/O vs. formula logic vs. tools) is designed to grow:
parameters, LOD expressions, aliases, bins/groups/sets, folders, number
formatting, descriptions, .hyper extracts / Tableau Hyper API, .twbx
support, documentation generation, BigQuery/dbt integration, dependency
inspection, and worksheet creation can each be added as new modules/tools
without rewriting the safe-write core.
MCP tools reference
| Tool | Purpose |
|---|---|
list_workbooks |
List .twb files in the authorized folder. |
inspect_workbook |
Datasources, worksheets, dashboards, fields (read-only). |
list_datasources |
Datasource metadata. |
list_fields |
Fields of a datasource (filter: all/dimension/measure/parameter/calculated). |
list_calculated_fields |
All calculated fields. |
validate_tableau_formula |
Validate a formula without writing. |
create_calculated_field |
Create one calculated field. |
update_calculated_field |
Update a field's formula (before/after report). |
delete_calculated_field |
Delete a calculated field (requires confirm=true). |
create_calculated_fields_batch |
Create many fields atomically (all-or-nothing). |
backup_workbook |
Manual timestamped backup. |
restore_workbook_backup |
List/restore backups (requires confirm=true). |
analyze_dataset |
Classify fields semantically and infer the business domain. |
suggest_business_metrics |
Suggest KPIs with Tableau formulas + rationale. |
create_recommended_metrics |
Create all recommended metrics atomically (1 backup). |
suggest_visualizations |
Recommend chart types per analysis, with justification. |
Analytics Copilot (semantic layer)
Beyond editing individual fields, the server can interpret a dataset and act like a Tableau analytics copilot — similar in spirit to Power BI Copilot, but producing native Tableau calculated fields.
Conversation flow with Claude Desktop:
> Analise meu dataset. → analyze_dataset
> Quais KPIs você recomenda? → suggest_business_metrics
> Crie todas as métricas recomendadas. → create_recommended_metrics
> Quais gráficos você recomenda? → suggest_visualizations
How it infers meaning (no fixed column names). Field names are normalized
(accent-free, camelCase-split, PT/EN) and matched against keyword sets plus
datatype and role signals — so ValorVenda, Revenue, Valor → revenue;
OrderID, PedidoID → identifier; OrderDate, Data → date; Lucro,
Profit → profit; Desconto → discount; Quantidade, Qty → quantity;
Estado, State, UF → geo. See semantics/field_classifier.py.
From that classification it proposes KPIs such as Receita Bruta/Líquida,
Lucro, Margem %, Ticket Médio, Pedidos, Clientes Únicos, Itens Vendidos,
Desconto Médio, Receita por Cliente/Produto/Categoria/Canal/Estado, Running
Total, MoM, YoY, Participação % — each with a Portuguese rationale and a native
Tableau formula (table calcs like Running Total/MoM/YoY are flagged with
requires_view_context). create_recommended_metrics reuses
create_calculated_fields_batch, so it inherits the same validate-all → single
backup → atomic write guarantees and is idempotent (existing captions are
skipped).
The new code lives under src/tableau_mcp/semantics/
(normalization, field_classifier, field_index, dataset_analyzer,
metric_catalog, visualization_advisor) and tools/analytics_tools.py.
License
MIT
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.