Tableau MCP / Tableau Langchain Intermediate

#DDQ2025-11 Tableau MCP Terminal Chat with Python and ChatGPT (Intermediate)

Familiarize yourself with Tableau MCP, and connect to Claude Desktop to ask questions to your published data sources.

C

Cristian Saavedra

Author

2025-11-12T10:27:55
9 min read
preview.png

Welcome to the DataDev Quest Challenge for November 2025! This challenge is designed to teach you how to use Tableau MCP and Tableau Developer Sandbox to build a terminal chat using Python and ChatGPT.

Challenge Overview

Objective:

Familiarize yourself with Tableau MCP, install it, build a terminal chat in Python, and ask an AI question using ChatGPT.

Why this challenge?

Tableau MCP is a suite of developer primitives, including tools, resources, and prompts that make it easier for developers to build AI applications that integrate with Tableau.

Learning Goals

  • Learn how to run a Python code with ChatGPT
  • Get your Tableau Developer Sandbox
  • Configure Personal Access Token (PAT)
  • Install and configure Tableau MCP

Submission Guidelines

  • Source Code: Publish your project publicly in your GitHub profile
  • Add README: This includes setup instructions and describes how to run the program.
  • Video of Solution: Include a video of your solution in the README file. You can publish it on YouTube and embed the iframe, or save the video file in the repository’s root directory.
  • Comments: Ensure your code is well-commented.
  • Submission: Submit your challenge in the following **forms**

Additional Resources


Before start

You must have installed Python and Visual Studio Code.
For this Challenge, you will need to:

  • Install Node.js version 22.7.5 or later
  • Configure Tableau MCP using the link
  • Create a ChatGPT Developer account
  • Create a ChatGPT API Token

I suggest you check the previous, beginner, and intermediate challenges on VizQL Data Services (VDS) for references on how this technology works in the backend. Also, there is a potential risk of sharing data with third parties. I recommend you check the terms of service before using enterprise data.


Getting Started

The first step is to get and set up your Tableau Developer Sandbox. My Medium Post provides a step-by-step guide to configuring it.

1. Explore your Tableau Cloud Site

In your Tableau Cloud Site, go to Explore

and open the Folder with the name Samples:

2. Be familiar with Superstore Datasource

In the folder Samples, click on Superstore Datasource and create a new Workbook Using This Data Source to familiarize yourself with the content, and you can do your own prompts and validate the answers with the data.

4. Enable and create TEST_MCP Personal Access Token (PAT)

Ensure you have enabled the Personal Access Token (PAT) in Settings/General/Enable personal access token, and configured your Personal Access Token (PAT). If you need help, follow my Medium Post, where I explain how to get it.

Create a token TEST_MCP and copy the Secret value:

4. Open a terminal or a Command Prompt and install Node.js

Verify if you have an existing version of Node.js, or install it using the link. For use with Tableau MCP, you must have version 22.7.5 or later.

CODE
node --version
v25.1.0

5. Git or Extract the Tableau MCP code in your local folder

Clone the Tableau MCP GitHub Repository with the command:

CODE
git clone https://github.com/tableau/tableau-mcp

If you are not familiar with git pull, an easier way to get the code is to go to the <> Code / Local / Download Zip and extract it into a local folder.

6. Open a terminal or Command Prompt and build the local Tableau MCP

Go to your local path, for my case, it is under my user / tableaumcp / tableau-mcp, and run the following commands. For more information, check the Tableau MCP documentation

Open a terminal: (CMD.exe in Windows or Terminal.app on MacOS) and execute the following commands, change to your local path:

CODE
cd /Users/csaavedra/tableaumcp/tableau-mcp
npm install
npm audit fix
npm run build

After building the folder, you will get a similar message as the following:

7. Modify the file config.stdio.json

Modify config.stdio.json with the full path to the local file build/index.js, the TEST_MCP token name as PAT_NAME, and the secret as PAT_VALUE, and the SERVER and SITE_NAME using the Tableau Cloud URL address.

As an example, my Tableau Cloud site is https://10ax.online.tableau.com/#/site/cristiansaavedradx, and my SERVER value is https://10ax.online.tableau.com, and my SITE_NAME is cristiansaavedradx

An example of what the file looks:

CODE
{
  "mcpServers": {
    "tableau": {
      "command": "node",
      "args": ["/Users/csaavedra/tableaumcp/tableau-mcp/build/index.js"],
      "env": {
        "TRANSPORT": "stdio",
        "SERVER": "https://10ax.online.tableau.com/",
        "SITE_NAME": "cristiansaavedradx",
        "PAT_NAME": "TEST_MCP",
        "PAT_VALUE": "0SueyEu7TNacnbX9XK6/Cw==:CUkQT56nVxIOkuYJXyEyt1HXEd2DhKDp",
        "DATASOURCE_CREDENTIALS": "",
        "DEFAULT_LOG_LEVEL": "debug",
        "INCLUDE_TOOLS": "",
        "EXCLUDE_TOOLS": "",
        "MAX_RESULT_LIMIT": "",
        "DISABLE_QUERY_DATASOURCE_FILTER_VALIDATION": "",
        "DISABLE_METADATA_API_REQUESTS": ""
      }
    }
  }
}

8. Check or Install Python

For this exercise, I recommend Python 3.12 or later. If you do not have Python installed, try using the Microsoft Store or the App Store, or download it from here. Open a terminal and execute the following command:

CODE
python --version

9. Create a folder for Python Application with local Libraries

Open a terminal and execute the following commands to create a folder for your Python Application:

CODE
cd /Users/csaavedra/tableaumcp/
mkdir tableau-mcp-int
cd tableau-mcp-int
python -m venv .venv

10. Activate the Python Virtual Environment

After executing the following command, you will get a (.env) at the beginning of the prompt line:

For Windows:

CODE
.venv\Scripts\activate

For MacOS:

CODE
source .venv/bin/activate

11. Update Pip and Install Libraries

Execute the following commands:

CODE
pip install --upgrade pip
pip install mcp langchain langgraph langchain-openai langchain-mcp-adapters langfuse python-dotenv

12. Copy config.stdio.json from Tableau MCP

In step 7, we modified the file config.stdio.json .Now, copy from the folder tableau-mcp to the folder tableau-mcp-int folder.

13. Create terminal_chat.py

Create a new file with the name terminal_chat.py in the folder tableau-mcp-int, and copy and paste the following code:

CODE
import os, sys, json, asyncio
from pathlib import Path
from dotenv import load_dotenv
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from langchain_mcp_adapters.tools import load_mcp_tools
from langchain_openai import ChatOpenAI
from langchain.agents import create_agent
from langchain_core.messages import HumanMessage
from langgraph.checkpoint.memory import InMemorySaver
from langfuse.langchain import CallbackHandler as LangfuseCallbackHandler

CONFIG_FILE = "config.stdio.json"
    
async def format_agent_response(agent, messages, langfuse_handler):
    """Stream response from agent and return the final text"""
    response_text = ""
    async for chunk in agent.astream(
        {"messages": messages},
        config={
            "configurable": {"thread_id": "main_session"},
            "callbacks": [langfuse_handler],
        },
        stream_mode="values",
    ):
        if "messages" in chunk and chunk["messages"]:
            latest_message = chunk["messages"][-1]
            if hasattr(latest_message, "content"):
                response_text = latest_message.content

    print()
    return response_text

async def main():
    here = Path(__file__).parent
    dotenv_path = here / ".env"
    if dotenv_path.exists():
        load_dotenv(dotenv_path=dotenv_path, override=False)
        print(f"Loaded .env from: {dotenv_path}")
    else:
        print("No .env file found — using shell env vars only")

    cfg_path = here / CONFIG_FILE
    if not cfg_path.exists():
        print(f"Config file not found: {cfg_path}", file=sys.stderr)
        sys.exit(1)

    with open(cfg_path, "r", encoding="utf-8") as f:
        cfg = json.load(f)

    if "mcpServers" not in cfg or "tableau" not in cfg["mcpServers"]:
        print("Invalid config: missing mcpServers.tableau", file=sys.stderr)
        sys.exit(1)

    tableau = cfg["mcpServers"]["tableau"]
    cmd = tableau.get("command")
    args = tableau.get("args", [])
    env = tableau.get("env", {})

    if not cmd or not args:
        print("Missing command/args in config", file=sys.stderr)
        sys.exit(1)
    if not os.path.exists(args[0]):
        print(f"MCP server entry not found: {args[0]}", file=sys.stderr)
        sys.exit(1)

    print("Loaded configuration from config.stdio.json")
    print(f" Command: {cmd} {' '.join(args)}")
    print(f" Transport: {env.get('TRANSPORT', 'stdio')}\n")

    server_params = StdioServerParameters(
        command=cmd,
        args=args,
        env=env
    )

    langfuse_handler = LangfuseCallbackHandler()

    print("Starting Tableau MCP via stdio ...")
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            mcp_tools = await load_mcp_tools(session)
            openai_key = os.getenv("OPENAI_API_KEY")
            if not openai_key:
                print("Set OPENAI_API_KEY in your .env", file=sys.stderr)
                sys.exit(1)

            llm = ChatOpenAI(model=os.getenv("OPENAI_MODEL", "gpt-4o-mini"), temperature=0)
            checkpointer = InMemorySaver()
            agent = create_agent(model=llm, tools=mcp_tools, checkpointer=checkpointer)

            print("Tableau MCP connected — (Ctrl+C, exit, bye to exit)\n")
            while True:
                try:
                    user = input("user_prompt> ").strip()
                    if not user:
                        continue
                    if user.lower() in {"exit", "quit", "bye"}:
                        print("Bye!")
                        break

                    messages = [HumanMessage(content=user)]
                    response_text = await format_agent_response(agent, messages, langfuse_handler)

                    print("Agent>" + response_text)
                    print("\n")

                except (KeyboardInterrupt, EOFError):
                    print("\nBye!")
                    break
                except Exception as e:
                    print(f"\nError: {e}", file=sys.stderr)

if __name__ == "__main__":
    asyncio.run(main())

13. Create .env file

Using Visual Studio Code, create a new empty file with the name .env , you should have something similar to this structure:

Now, copy and paste the following code into the .env file and replace the values with the API Keys:

CODE
# Langfuse
LANGFUSE_SECRET_KEY = "sk...Replace with your info"
LANGFUSE_PUBLIC_KEY = "pk...Replace with your info"
LANGFUSE_BASE_URL = "https://us.cloud.langfuse.com"

# Model Providers
OPENAI_API_KEY='sk...Replace with your info'
OPENAI_MODEL=gpt-4o-mini

For Langfuse, go to langfuse.com, create a project > Settings > API Keys, and create and copy the API Keys:

For OpenAI, go to platform.openai.com create an account, add credit (money), select the gear (Settings), API Keys, and Create new secret key:

14. Run the terminal_chat.py

From the terminal, now run the following command, and you can ask questions to your data using ChatGPT with Python:

CODE
python terminal_chat.py


Challenge

Your challenge is to explore prompts that use different tools listed in the tableau MCP, give a list of prompts, and identify which tools were used.

Check the complete list of tools available for Tableau MCP and select prompts that bring at least three tools.

DataDev Rules!

Have you already solved it?

How can you show the tool that the Agent selected?
For example: “List all data sources” used list-datasources tool


Who am I?

Hey, I forgot to introduce myself. I am Cristian Saavedra Desmoineaux, a Tableau Visionary & Data Dev Ambassador, and co-founder of DataDevQuest. I am passionate about solving data problems and have over 20 years of experience in complex scenarios. You can contact me on LinkedIn.

DataDev Rules!


Special Thanks

I want to give special thanks to Will Sutton. Through this and the Tableau Langchain project, what started as teamwork turned into a friendship, and I’m very thankful for that. And of course, my gratitude to Joe Constantino, whose support made this possible.