2026 Devcon Workshop: Talk to NRDS: Building an LLM Chat for TethysDash to Explore the NextGen Research Data Stream
Welcome! In this hands-on workshop we will connect a chatbox-powered LLM to TethysDash, ask it to build hydrology dashboards by chatting, talk to the production NRDS MCP server to query and plot real forecast files, and finish by writing a few lines of Python to bring our own MCP tool to life.
If at any point your LLM is hallucinating - it can happen :/ - that is part of the experience. We will tweak prompts together.
Hydrologists who are curious about LLMs and MCP tools and want to see what they can actually do with their data - without having to become a software engineer overnight. Familiarity with Python and Jupyter notebooks helps. You do not need to know Docker, React, or Django.
We have about 2 hours together. Here is the path:
- Get the workshop environment running. At the in-person event your host has a VM ready for you - just connect and skip ahead. If you are doing this on your own laptop, or you are the maintainer setting up VMs, start at Setup.md and pick the path that matches.
- Build a shared mental model for what an LLM, an MCP tool, context, tokens, embeddings, and prompt templates actually are - with some Digimon analogies, because why not. See Definitions.md.
- Use the chatbox inside TethysDash to build a couple of dashboards from the official TethysDash tutorials. See MakingDashboards.md.
- Talk to the production NRDS MCP server - fetch forecast files from S3, plot time series, generate statistics tables and cards. See NRDSExploration.md.
- Open the hood: a workshop-local MCP server (
devcon_mcp) is wired up and waiting for eight lines of Python. Fill them in to unlock the tools. See devcon_mcp/README.md.
By the end you should be comfortable: opening the TethysDash chatbox, connecting an MCP server, prompting the LLM to build a dashboard, querying NRDS hydrology data through the LLM, and editing a Python function inside a running MCP server.
You should have:
-
An API key from one of the three supported LLM providers - the chatbox talks to all three, pick whichever you already have an account with:
- Ollama (the workshop default) - free tier covers the session: ollama.com
- OpenAI - pay-per-token: platform.openai.com
- Anthropic (Claude) - pay-per-token: console.anthropic.com
Cost-aware model picks for each provider are in Definitions.md > Picking your Digimon. Set a monthly spend cap on OpenAI/Anthropic before you start.
-
A laptop with
ssh(any OS - Linux, macOS, or Windows with WSL / Git Bash). -
The SSH login and the
code-serverpassword for your workshop VM - your workshop host will give you both.
Optionally, you can use VS Code on your laptop instead of code-server in the browser. Either works; pick the one you are happiest with.
Start at Setup.md and pick the path that matches your situation (in-person VM, self-study on your laptop, or workshop maintainer). When the stack is up and you see tethysdash, tethysdash_mcps, and devcon_mcp all reporting healthy, come back and continue with Definitions.md.
Note If you get stuck during the workshop, raise your hand. We are here to debug live. If the LLM responds with something that does not match what we are doing, do not panic - try rephrasing the prompt, or follow the manual tutorial steps while you tweak the wording.
You have the SSH login and the code-server password from your workshop host. You do not need to run docker, git, npm, or any setup script - the host did all of that. Here is your full path to a working chatbox:
ssh user@vm-host
bash ~/workshops/devcon/scripts/tunnel.sh
# Copy the `ssh -L ...` line it prints, then:
exitIt looks like this (your host + your user will differ):
ssh -L 8000:127.0.0.1:8000 \
-L 8080:127.0.0.1:8080 \
-L 9001:127.0.0.1:9001 \
-L 9003:127.0.0.1:9003 \
-o ServerAliveInterval=30 -o ServerAliveCountMax=3 \
user@vm-hostKeep this terminal open for the whole workshop - closing it kills the tunnel and your browser tabs stop working.
http://localhost:8000- TethysDash (default login:admin/pass)http://localhost:8080- code-server (paste the password your host gave you)
Or skip the code-server tab and use VS Code on your laptop with the Remote - SSH extension pointed at the VM. Either works.
Note Lost the code-server password? It is auto-generated per-VM by the installer (no shared default), and lives in plaintext at
~/.config/code-server/config.yamlon the VM. Ask your host - they can recover it withgrep '^password:' ~/.config/code-server/config.yaml. Or use VS Code Remote-SSH and skip code-server entirely - no password needed for that path.
In TethysDash → chatbox sidebar → gear icon → MCP servers, add three URLs:
http://localhost:9001/mcp- tethysdash_mcpshttp://localhost:9003/mcp- devcon_mcphttps://nrds-mcps-43707369422.us-central1.run.app/mcp- nrds (the public production server, used in NRDSExploration.md)
Then in chatbox → gear icon → Provider, plug in your API key (Ollama / OpenAI / Anthropic - whichever one you brought, see Definitions.md > Picking your Digimon).
In code-server (or VS Code Remote-SSH), open ~/workshops/devcon/devcon_mcp/challenges.py. Edit, save - the MCP server hot-reloads automatically. No docker compose restart, no rebuild, no nothing. Just save.
Your edits show up in two places. Keep both visible while you work on a challenge.
Server logs - in code-server's integrated terminal (Ctrl+`) or in a fresh SSH session, run:
cd ~/workshops/devcon
docker compose logs -f devcon_mcpWhen you save challenges.py, you should see a watching / reload / restarting line within 1-2 seconds. When the LLM calls your tool, you see Tool ... called. If your code has a Python error, the traceback streams here - read it, fix, save again, the watcher retries automatically. The full diagnostic-log key (which line means what for each challenge) lives in devcon_mcp/README.md.
Chatbox live activity - in TethysDash, ask the LLM to call the tool you just edited. The chatbox shows a small live-activity strip while a tool is running, and a clear error envelope if the tool fails. Behavior changes from your edits show up here immediately - no page refresh needed.
Everything else is chat the LLM in TethysDash + edit challenges.py + watch the logs. The flow through the workshop content from here is:
Definitions.md → MakingDashboards.md → NRDSExploration.md → devcon_mcp/README.md.
| Symptom | What to do |
|---|---|
| Tunnel terminal died | Re-paste the same ssh -L ... command in a fresh terminal |
devcon_mcp got wedged from a syntax error |
Tail the logs (step 6 above) to find the traceback, fix the Python error in challenges.py and save - watchfiles retries. If it is truly stuck: ssh user@vm-host bash ~/workshops/devcon/scripts/restart-mcp.sh devcon_mcp |
Browser shows nothing on http://localhost:8000 |
Confirm your tunnel terminal is still alive |
| One MCP server connects (green ✓) but another shows "Connection failed" (red ✗) | Your ssh -L ... tunnel is missing the failing port. The default tunnel.sh forwards 8000, 8080, 9001, 9003 - if you customized the command by hand (e.g., adding -L 9000 for a local nrds_mcps), make sure you did not drop one of the defaults. Quick test from the laptop: curl http://localhost:<port>/health - if it fails or hangs, the tunnel is missing that port. Re-run bash scripts/tunnel.sh on the VM to get the full canonical command. |
| Chatbox responds weirdly | Check you picked a recommended model (see Definitions.md > Picking your Digimon); LLM hallucinations are normal :/ - try rephrasing |
| Chatbox shows an MCP server as connected but "this server exposes no tools" | Run ssh user@vm-host bash ~/workshops/devcon/scripts/smoke.sh - it asserts the wire-level tools/list count per server. If smoke fails on devcon_mcp, ask your host to confirm the workshops repo is at the post-ebb23be state (introduces devcon_mcp/__main__.py and the matching docker-compose.yml command edit). |
The stack on your VM:
- TethysDash (Django app + chatbox UI) -
127.0.0.1:8000. The dashboard environment we will be talking to. - tethysdash_mcps (visualization-layer MCP server) -
127.0.0.1:9001. Gives the LLM tools to create maps, add layers, plot time series, etc. inside TethysDash. - devcon_mcp (the workshop's own MCP server - this is the one you edit) -
127.0.0.1:9003. Where the coding challenges live. - NRDS production MCP server -
https://nrds-mcps-43707369422.us-central1.run.app/mcp. Public, already deployed. Provides the NextGen Research Data Stream tools. - code-server (browser VS Code, installed on the VM) -
127.0.0.1:8080. Optional; skip if you prefer your local editor.
At the in-person event all local ports bind to loopback on the VM, and participants reach them through an SSH tunnel from their laptop - Setup-VM.md covers the tunnel. If you are running locally, everything is already on your own localhost; no tunnel needed.
| File | What is it for |
|---|---|
| Setup.md | Router. Pick VM-operator or local-laptop and follow the link. Start here. |
| Setup-VM.md | Per-participant VM setup on NSF ACCESS (workshop maintainer's guide). SSH tunnels, code-server, day-of operations. |
| Setup-Local.md | Run the workshop on your own laptop. No SSH tunnel, no code-server; everything on localhost. |
| Definitions.md | AI concepts (LLM, MCP, context, tokens, embeddings, ...) with Digimon analogies, and a tour of TethysDash building blocks. |
| MakingDashboards.md | Building dashboards by chatting - walks through TethysDash's RFC Max Stage and GEOGLOWS tutorials. |
| NRDSExploration.md | Plotting forecast time series, statistics tables, and statistics cards using the production NRDS MCP server. |
| devcon_mcp/README.md | Four small coding challenges that unlock a working MCP server you can call from the chatbox. |
If you are setting up a participant VM, Setup-VM.md is your starting point. The companion files cover the runtime knobs: .env.example for per-VM pins, scripts/setup.sh for cloning the source repos and pulling the workshop image, scripts/restart-mcp.sh and scripts/reset-repos.sh for day-of recovery, and scripts/tunnel.sh for generating the SSH tunnel command you hand to each participant.
The workshop image (ghcr.io/aquaveo/ciroh-devcon-2026:${IMAGE_TAG}) is published by .github/workflows/publish-image.yml on each v* tag push and bundles TethysDash + the four FIRO-Tethys plugins (ciroh_plugins, tethysdash_plugin_cnrfc, tethysdash_examples, RFC-Plugins). Bump the tag, push, wait for CI, then update IMAGE_TAG in .env.example before the event.
MIT. The workshop content and scripts in this repo are MIT. The consumed repos (TethysDash, the MCP servers, the FIRO-Tethys plugin catalog) keep their own licenses.