Skip to main content

Python SDK for Tableau Hosted MCP

Project description

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

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-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-mcp login              # run the OAuth flow, persist tokens
tableau-mcp logout             # clear stored tokens (recovery for stale-cache errors)
tableau-mcp doctor             # diagnose config + connectivity (CIMD reachable, MCP
                               #   reachable, live list_tools round-trip)
tableau-mcp list-tools         # list available MCP tools
tableau-mcp call-tool NAME [--arg key=value ...] [--json '{"k": "v"}']
                               # invoke a tool, print JSON result
tableau-mcp playground         # open the Streamlit UI playground (requires [playground] extra)
tableau-mcp --version
tableau-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 APIinput_schema field on tool specs
  • OpenAI APIparameters 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-contentget-workbooklist-viewsget-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-mcp playground

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-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-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-mcp doctor. It probes CIMD reachability, MCP endpoint reachability, and full auth round-trip, and points at whichever step failed.

Tokens on the wrong tenanttableau-mcp logout clears the stored token for the current TABLEAU_MCP_SERVER_URL value; run it before switching tenants.

License

MIT. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tableau_hosted_mcp-0.1.0a1.tar.gz (24.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tableau_hosted_mcp-0.1.0a1-py3-none-any.whl (24.0 kB view details)

Uploaded Python 3

File details

Details for the file tableau_hosted_mcp-0.1.0a1.tar.gz.

File metadata

  • Download URL: tableau_hosted_mcp-0.1.0a1.tar.gz
  • Upload date:
  • Size: 24.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tableau_hosted_mcp-0.1.0a1.tar.gz
Algorithm Hash digest
SHA256 cc90d5bd2dd2e2f0f3aaa33cf493bcb25e22a76ff5270a82c6423cc3728073ae
MD5 2035a17af0b343189c1ac3dc2cecb54c
BLAKE2b-256 de04bc6e719d59aab806822dac3c74e5e813711f5b6ec2f3020653730ac53b25

See more details on using hashes here.

Provenance

The following attestation bundles were made for tableau_hosted_mcp-0.1.0a1.tar.gz:

Publisher: publish.yml on mok3bat/tableau-hosted-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tableau_hosted_mcp-0.1.0a1-py3-none-any.whl.

File metadata

File hashes

Hashes for tableau_hosted_mcp-0.1.0a1-py3-none-any.whl
Algorithm Hash digest
SHA256 2810295e552f126d61b9888902c524a15e92e4b9a71d64971b2a9634676cac1e
MD5 b5b33ab6e26e4f401130baa710071564
BLAKE2b-256 1d6ea98721e7e1dd343acdacfa8f69c99c5aeea917db22f532a0098efe1f5cd4

See more details on using hashes here.

Provenance

The following attestation bundles were made for tableau_hosted_mcp-0.1.0a1-py3-none-any.whl:

Publisher: publish.yml on mok3bat/tableau-hosted-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page