Skip to main content

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, not uv add. The base SDK installs cleanly under either tool, but the [playground] extra pulls in Streamlit, which depends on pyarrow. uv add "tableau-hosted-mcp[playground]" runs in project mode and can decide to build pyarrow from source — that build often fails on macOS when Anaconda is on your PATH because CMake picks up Anaconda's outdated libarrow.dylib and tries to link a newer PyArrow against it. Plain pip install grabs pre-built wheels and avoids the whole issue. If you want to stay on uv, use uv pip install --only-binary=:all: "tableau-hosted-mcp[playground]" inside a venv instead of uv 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:

  1. Constructor kwargs — Settings(server_url=..., verify_ssl=False)
  2. Environment variables (prefix TABLEAU_MCP_):
    • TABLEAU_MCP_SERVER_URL (default https://mcp.tableau.com)
    • TABLEAU_MCP_CIMD_URL (default your CIMD client.json URL)
    • TABLEAU_MCP_CONFIG_DIR
    • TABLEAU_MCP_LOG_LEVEL
    • TABLEAU_MCP_VERIFY_SSL
    • TABLEAU_MCP_TIMEOUT
  3. .env file in the current working directory
  4. 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_schema field on tool specs
  • OpenAI API — parameters field on function specs
  • Any framework built on those primitives (LangChain, LlamaIndex, etc.)

The pattern for a manual agent loop is:

  1. tools = await client.list_tools() — the 24 Tableau MCP tools
  2. Convert each Tool to your LLM's tool spec (change the wrapper keys)
  3. Pass the user's question + the tool specs to the LLM
  4. LLM responds with tool_use blocks
  5. For each tool_use, await client.call_tool(name, arguments)
  6. Feed the tool results back as the next user message
  7. 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 install here — do not uv add "tableau-hosted-mcp[playground]". Streamlit's pyarrow dependency can trigger an hour-long CMake build from source that fails on any Mac with Anaconda installed. pip install (or uv 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 doctor plus token cache contents.
  • Settings — resolved effective values, which env vars are present, and the .env file'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:

  1. Switch to pip install "tableau-hosted-mcp[playground]" in a python -m venv — this uses PyArrow's pre-built wheel.
  2. Or force wheel-only mode with uv:
    uv pip install --only-binary=:all: "tableau-hosted-mcp[playground]"
    
    Unset CONDA_PREFIX first if Anaconda's libarrow still 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.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tableau-hosted-mcp 0.1.1
File Size Uploaded Provenance Actions
tableau_hosted_mcp-0.1.1.tar.gz 26.3 kB With provenance Details for tableau_hosted_mcp-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for tableau-hosted-mcp 0.1.1
File Interpreter ABI Platform Actions
tableau_hosted_mcp-0.1.1-py3-none-any.whl Python 3 none any Details for tableau_hosted_mcp-0.1.1-py3-none-any.whl

Total release size: 51.2 kB

Release files / tableau_hosted_mcp-0.1.1.tar.gz

Download URL tableau_hosted_mcp-0.1.1.tar.gz
Size 26.3 kB
Tags Source
SHA-256 checksum
How to use checksums
d75671f6a6a8bd658562962cf9387dbf66cdf94ecf7ee573755a675fc91816af
BLAKE2b-256 checksum
How to use checksums
e0888a435b47c4e980299de8fa5da135b826b3fe094a10872af7acb182be03cf
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 log

Release files / tableau_hosted_mcp-0.1.1-py3-none-any.whl

Download URL tableau_hosted_mcp-0.1.1-py3-none-any.whl
Size 24.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b893c72af16902f7b67330fae66729255736c82d9cfd4b2d5cf7b6b393e702d9
BLAKE2b-256 checksum
How to use checksums
5747e5c7b338a4c3cc6fdcbf87c7f8a22cd8db7e043c0695d056ebd999ca460f
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 log

Security for tableau-hosted-mcp 0.1.1

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.

Select from 2 files

Attestation for tableau_hosted_mcp-0.1.1.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 log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page