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

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.0
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)

Table of built distributions (wheels) for tableau-hosted-mcp 0.1.0
File Interpreter ABI Platform Actions
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 log

Release 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 log

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

Select from 2 files

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