tableau-hosted-mcp
Python SDK for Tableau Hosted MCP. Handles OAuth 2.1
PKCE via a CIMD-registered public client, persists tokens locally, and exposes
both an async and sync API plus a tableau-hosted-mcp CLI. Ready to drop into an LLM
agent — the MCP tool schemas map 1:1 to Anthropic and OpenAI tool-use formats.
Install
pip install tableau-hosted-mcp
Requires Python 3.11+.
Recommended:
pip, notuv add. The base SDK installs cleanly under either tool, but the[playground]extra pulls in Streamlit, which depends onpyarrow.uv add "tableau-hosted-mcp[playground]"runs in project mode and can decide to buildpyarrowfrom source — that build often fails on macOS when Anaconda is on yourPATHbecause CMake picks up Anaconda's outdatedlibarrow.dyliband tries to link a newer PyArrow against it. Plainpip installgrabs pre-built wheels and avoids the whole issue. If you want to stay onuv, useuv pip install --only-binary=:all: "tableau-hosted-mcp[playground]"inside a venv instead ofuv add.
First run
The first call opens a browser tab to sso.online.tableau.com. Sign in once —
tokens are persisted to the platform's user-config directory (resolved via
platformdirs) and reused on
subsequent runs.
| Platform | Default cache directory |
|---|---|
| macOS | ~/Library/Application Support/tableau-hosted-mcp/ |
| Linux | ~/.config/tableau-hosted-mcp/ (respects $XDG_CONFIG_HOME) |
| Windows | %LOCALAPPDATA%\tableau-hosted-mcp\tableau-hosted-mcp\ |
Override with the TABLEAU_MCP_CONFIG_DIR env var if you want tokens on an
encrypted volume, a shared drive, or an ephemeral location. Storage is a
SQLite-backed key-value store (via diskcache under py-key-value-aio); no
native binaries, works identically on all three platforms.
The easiest way to warm up the token cache is:
tableau-hosted-mcp login
Library usage (async)
import asyncio
from tableau_hosted_mcp import TableauHostedClient
async def main():
async with TableauHostedClient() as client:
tools = await client.list_tools()
print(f"{len(tools)} tools available")
result = await client.call_tool("list-projects", {})
# result is fastmcp.client.client.CallToolResult:
# .is_error: bool
# .content: list[mcp.types.ContentBlock] (text/image/resource)
# .structured_content: dict | None
# .data: Any
print(result.content[0].text)
asyncio.run(main())
Also available: list_resources(), list_prompts(), and .fastmcp for direct
access to the underlying fastmcp.Client if you need something the wrapper
doesn't expose.
Library usage (sync)
For scripts, notebooks, Django management commands, or anything without an event loop:
from tableau_hosted_mcp import TableauHostedClientSync
with TableauHostedClientSync() as client:
tools = client.list_tools()
result = client.call_tool("list-projects", {})
The sync facade runs a dedicated background thread with its own asyncio loop. The connection persists across method calls — you pay OAuth cost once, not per call. Do not share a single instance across threads.
CLI
tableau-hosted-mcp login # run the OAuth flow, persist tokens
tableau-hosted-mcp logout # clear stored tokens (recovery for stale-cache errors)
tableau-hosted-mcp doctor # diagnose config + connectivity (CIMD reachable, MCP
# reachable, live list_tools round-trip)
tableau-hosted-mcp list-tools # list available MCP tools
tableau-hosted-mcp call-tool NAME [--arg key=value ...] [--json '{"k": "v"}']
# invoke a tool, print JSON result
tableau-hosted-mcp playground # open the Streamlit UI playground (requires [playground] extra)
tableau-hosted-mcp --version
tableau-hosted-mcp --log-level DEBUG # verbose httpx + MCP protocol logs
Every subcommand honours the same env-var-driven config as the library API (see below).
Configuration
Settings reads from four sources, highest precedence first:
- Constructor kwargs —
Settings(server_url=..., verify_ssl=False) - Environment variables (prefix
TABLEAU_MCP_):TABLEAU_MCP_SERVER_URL(defaulthttps://mcp.tableau.com)TABLEAU_MCP_CIMD_URL(default your CIMDclient.jsonURL)TABLEAU_MCP_CONFIG_DIRTABLEAU_MCP_LOG_LEVELTABLEAU_MCP_VERIFY_SSLTABLEAU_MCP_TIMEOUT
.envfile in the current working directory- Built-in defaults
Settings.from_file(path, **overrides) also loads a JSON config file.
Exceptions
from tableau_hosted_mcp import (
TableauHostedMCPError, # base — catch this to handle any SDK failure
ConfigurationError, # invalid settings
AuthenticationError, # OAuth failed even after auto-retry with cleared tokens
ConnectionError, # transport failure reaching the MCP server (httpx-level)
)
connect() automatically retries once on the stale-cache OAuth 500 pattern
by clearing the token store and re-authenticating. If that retry also fails,
you get an AuthenticationError. That means the manual logout && login
recovery step is normally invisible to callers.
Using it inside an LLM agent
The MCP tool schemas exposed by client.list_tools() are plain JSON Schema
objects (mcp.types.Tool.inputSchema). They map 1:1 to the tool-use formats
used by:
- Anthropic API —
input_schemafield on tool specs - OpenAI API —
parametersfield on function specs - Any framework built on those primitives (LangChain, LlamaIndex, etc.)
The pattern for a manual agent loop is:
tools = await client.list_tools()— the 24 Tableau MCP tools- Convert each
Toolto your LLM's tool spec (change the wrapper keys) - Pass the user's question + the tool specs to the LLM
- LLM responds with tool_use blocks
- For each tool_use,
await client.call_tool(name, arguments) - Feed the tool results back as the next user message
- Loop until the LLM returns a final text answer
See examples/04_claude_agent.py for a complete working implementation.
Examples
The examples/ directory contains runnable scripts:
| File | What it shows |
|---|---|
01_quickstart_async.py |
Connect, list tools, call list-projects. |
02_quickstart_sync.py |
Same flow with the sync facade — no asyncio in your code. |
03_data_pipeline.py |
Chain search-content → get-workbook → list-views → get-view-data. Pure-SDK, no LLM. |
04_claude_agent.py |
Wire the MCP tools into an Anthropic Claude agent loop. |
05_openai_compat_agent.py |
Same loop against any OpenAI-compatible endpoint (OpenAI, Groq, Together, Ollama, LM Studio, …). |
Run any of them from the repo root:
python examples/01_quickstart_async.py
The Claude example needs pip install anthropic and
export ANTHROPIC_API_KEY=sk-ant-....
The OpenAI-compat example needs pip install openai and OPENAI_API_KEY.
Point at a non-OpenAI endpoint via OPENAI_BASE_URL, e.g.
export OPENAI_BASE_URL=http://localhost:11434/v1 # Ollama
export OPENAI_MODEL=qwen2.5-coder:14b
export OPENAI_API_KEY=ollama # placeholder, endpoint ignores it
python examples/05_openai_compat_agent.py
Playground
A Streamlit UI for exploring the SDK interactively — pick tools from a dropdown, run them with an auto-generated form, chat with an LLM that has the tools wired up, and inspect diagnostics + settings.
Install and launch:
pip install "tableau-hosted-mcp[playground]"
tableau-hosted-mcp playground
Stick with
pip installhere — do notuv add "tableau-hosted-mcp[playground]". Streamlit'spyarrowdependency can trigger an hour-long CMake build from source that fails on any Mac with Anaconda installed.pip install(oruv pip install --only-binary=:all:) uses the pre-built wheels PyArrow publishes and finishes in seconds.
Four tabs:
- Tool Explorer — every MCP tool's JSON schema becomes a form; run it and see JSON / images / structured content rendered in place.
- Agent Chat — switch between Anthropic (Claude) and OpenAI-compatible endpoints, watch each tool call unfold in a collapsible expander.
- Diagnostics — live version of
tableau-hosted-mcp doctorplus token cache contents. - Settings — resolved effective values, which env vars are present, and
the
.envfile's contents.
Set ANTHROPIC_API_KEY and/or OPENAI_API_KEY (and OPENAI_BASE_URL for
non-OpenAI endpoints) before launching if you want the chat tab.
Troubleshooting
OAuth 500 from sso.online.tableau.com — the SDK auto-retries after
clearing the token cache; you should rarely see this bubble up. If it persists,
run tableau-hosted-mcp logout and try again, or open your CIMD document
(TABLEAU_MCP_CIMD_URL) in a browser to confirm it's serving valid JSON.
"Client failed to connect" with a stack trace — run tableau-hosted-mcp doctor.
It probes CIMD reachability, MCP endpoint reachability, and full auth
round-trip, and points at whichever step failed.
Tokens on the wrong tenant — tableau-hosted-mcp logout clears the stored token
for the current TABLEAU_MCP_SERVER_URL value; run it before switching
tenants.
uv add "tableau-hosted-mcp[playground]" fails building pyarrow —
CMake picks up an old libarrow.dylib from Anaconda and the build breaks
with Could not find a package configuration file provided by "ArrowCompute". Two fixes:
- Switch to
pip install "tableau-hosted-mcp[playground]"in apython -m venv— this uses PyArrow's pre-built wheel. - Or force wheel-only mode with uv:
uv pip install --only-binary=:all: "tableau-hosted-mcp[playground]"
UnsetCONDA_PREFIXfirst if Anaconda'slibarrowstill gets picked up.
Harmless pip dependency warnings after install — messages like
fastapi 0.115.0 requires starlette<0.39.0, but you have starlette 1.3.1
or pyopenssl 24.2.1 requires cryptography<44 come from other packages
already present in your parent Python environment (often from Anaconda).
The SDK does not use fastapi or pyopenssl; if the "Successfully
installed … tableau-hosted-mcp-…" line appears, the install worked. Run
tableau-hosted-mcp --version to confirm.
License
MIT. See LICENSE.
Metadata
Release files for tableau-hosted-mcp 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| tableau_hosted_mcp-0.1.0.tar.gz | 26.3 kB | With provenance | Details for tableau_hosted_mcp-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| tableau_hosted_mcp-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for tableau_hosted_mcp-0.1.0-py3-none-any.whl |
Total release size: 51.2 kB
Release files / tableau_hosted_mcp-0.1.0.tar.gz
| Download URL | tableau_hosted_mcp-0.1.0.tar.gz |
|---|---|
| Size | 26.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
95ddf1acb22b81bcc37c9b421b54acb0eb8bf7d147a2f7845ff7927d7b0d2c3b
|
|
BLAKE2b-256 checksum How to use checksums |
459eda5a051b4ca2f293ea54c03cb146fd4feee1621dd74f32cae51ac5c33e67
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 18, 2026.
Transparency logRelease files / tableau_hosted_mcp-0.1.0-py3-none-any.whl
| Download URL | tableau_hosted_mcp-0.1.0-py3-none-any.whl |
|---|---|
| Size | 24.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a3af8c741186a5154f0c8dbc1c4b52e312ec15566310d4cd6c569131b5472a81
|
|
BLAKE2b-256 checksum How to use checksums |
f9efa6a8d976b227ade919332bb269eb7e336902c3ba61a42b52814218a2b10c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 18, 2026.
Transparency logSecurity for tableau-hosted-mcp 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for tableau_hosted_mcp-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 18, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.