udisc
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@udisc Which holes at my home course cost me the most strokes?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
disc-golf-trends
Your disc golf rounds and practice throws, from your own exports. It has three parts: a read-only
MCP server that lets Claude answer questions about your rounds, courses and
scoring trends, an offline HTML dashboard of the same data, and a website where anyone can pick their own
CSV and see the dashboard in the browser. (Formerly disc-golf-journal and udisc-mcp. Not affiliated with UDisc or
TechDisc.)
No credentials and no network access. UDisc has no public API, and its Terms prohibit scraping, so this project never contacts UDisc or TechDisc. It only reads the CSV files their apps export. Nothing is modified.
Get your data
In the UDisc mobile app: You tab → menu (top right) → Rounds → menu (top right) → Export to CSV.
Move the file into a folder of your choice. One export holds your whole history; re-export now and then to pick up
new rounds. Every *.csv in the folder is read and overlapping exports are de-duplicated, so you can drop new
exports next to old ones.
Related MCP server: FitNotes MCP Server
Setup
Python 3.10 or newer.
git clone https://github.com/cin/disc-golf-trends.git
cd disc-golf-trends
python -m venv .venv
.venv\Scripts\pip install -e ".[dev]" # Linux/macOS: .venv/bin/pip
copy .env.example .env # then edit it.env (see .env.example):
Variable | Meaning |
| Folder holding your export(s) |
| Your name exactly as in the |
| Optional. Folder holding a TechDisc throw export; may be the same folder as |
| Optional. Rounds a hole needs before |
The .env file is git-ignored. It holds only paths and a name, no secrets. The server exits at startup with a clear
message if UDISC_CSV_DIR or UDISC_PLAYER is missing or MIN_HOLE_ROUNDS is invalid, and on Linux/macOS if .env
is readable by other users (chmod 600 .env). A setting that goes wrong later (say the folder is deleted) fails that
one tool call, not the server.
Register with Claude
Claude Code:
claude mcp add udisc -- C:\path\to\disc-golf-trends\.venv\Scripts\python C:\path\to\disc-golf-trends\udisc_mcp.pyClaude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"udisc": {
"command": "C:\\path\\to\\disc-golf-trends\\.venv\\Scripts\\python.exe",
"args": ["C:\\path\\to\\disc-golf-trends\\udisc_mcp.py"]
}
}
}Tools (all read-only)
Tool | Answers |
| How fresh is the data? Newest round, export age, stale warning |
| Rounds by date range and course, newest first |
| Hole-by-hole scores vs par for one round |
| Courses, layouts, rounds played, first/last played |
| Average/best/worst rounds, per-layout stats, 3 best and worst holes; |
| Score vs par by month or season, optionally per course |
| Share of holes at eagle or better, birdie, par, bogey, double, triple or worse; overall, by month or by season |
| Holes that cost the most strokes over par, with blow-up counts (a blow-up is a double bogey or worse) |
| Two calendar years side by side |
| TechDisc: throw count, first/last throw, export age, stale warning |
| TechDisc: throws by date range, Backhand/Forehand and Hyzer/Flat, newest first |
| TechDisc: speed, distance, spin and angle averages/maxima, overall and by throw style |
| TechDisc: those numbers by month or season |
Dates (start, end) are YYYY-MM-DD and inclusive; anything else is rejected with a message instead of silently
returning the wrong rounds. group_by accepts only month or season. list_rounds and list_throws return at most
500 rows.
Example questions: "Which holes at my home course cost me the most strokes?", "How has my scoring trended each season?", "Compare 2025 and 2026."
Dashboard
.venv\Scripts\python dashboard.py # writes dashboards\udisc-dashboard.htmlOne self-contained HTML file with three tabs, opened by double-clicking it: Scoring (strokes over par and round rating by month, quarter or season, with the best and worst periods ringed; every round as a dot, with new personal bests at a course in blue; the three costliest holes highlighted; birdies and blow-ups per round; the share of holes at each score by season; a courses table), Holes (where the strokes go on a chosen course, and a hole-by-season heatmap) and Throws (TechDisc speed and distance trends). Filters at the top scope the whole page by course and date range; "Highlight season" shades one season on the charts and dims the others without filtering anything. Every chart has a table view. Holes shows one course at a time, so with "All courses" it asks you to pick one (and offers your most played). Throws only uses the date range, and says so next to the greyed-out course and season controls.
Themes (menu at the top right, remembered in the browser): System follows your light or dark setting,
Mocha is Catppuccin Mocha, and the third entry is the opposite of your system look (Dark on a light system, Light
on a dark one). ?theme=system, ?theme=mocha, ?theme=dark or ?theme=light in the address picks one; dark or
light only applies when it is the opposite of the system look. It makes no network requests and has no animation. Re-run the command after each new export.
--player "Name" builds the same dashboard for another player in your export (name matched exactly, ignoring case),
written to dashboards\udisc-dashboard-name.html. The export only holds rounds you scored together, so that
dashboard covers those rounds, and your TechDisc throws are left out.
The file embeds real rounds, so dashboards/ is git-ignored. Each file holds one player's rows.
Website
Live at https://cin.github.io/disc-golf-trends/. site/ is the same dashboard as a static web page: a visitor
picks their own UDisc and/or TechDisc CSV export and sees the charts. Nothing is uploaded. The files are read in the
browser tab, and the built page's content security policy stops the page from making network requests. It is written
in TypeScript (a port of udisc_data.py and techdisc_data.py) and draws with the same dashboard_template.html as
the Python dashboard, so there is one set of charts to maintain. A scorecard export includes everyone you played
with; the page asks whose rounds to show and starts with the player who has the most. "Try it with sample data" loads
made-up rounds and throws. The two setup cards (choose files, choose player) minimize to a one-line summary once you
have picked, so the dashboard starts near the top; click a summary to change the choice, or drop new files on the
picker even while it is minimized.
Working on it needs Node.js 22.12 or newer (24 recommended); the Python parts do not need Node.
cd site
npm install
npm run dev # http://localhost:5173/, add ?sample to load made-up data
npm test # unit tests, plus a check that the port gives the Python implementation's answers
npm run build # static files in site/dist/The port is held to the Python code by tests/golden.py: it runs every query over the fixtures (including a larger
made-up dataset) and writes site/tests/golden.json, and the TypeScript tests must reproduce it exactly. After
changing the Python statistics, run .venv\Scripts\python tests\golden.py and commit the result; a Python test fails
when the file is stale. Pushes to main that touch site/, the dashboard template or the fixtures are published to
GitHub Pages by .github/workflows/pages.yml.
Data notes
The export has a
Parrow before each round's player rows. Par comes from there.A score of
0means the hole was not played. Rounds with no scores are ignored, and partial rounds are left out of averages and trends.Totals are the sum of the hole scores. The file's
Totalcolumn is only a cross-check: when you scratch a hole ("-" in the app), UDisc still writes a placeholder score in that hole's column but leaves it out of its own total. If the holes add up to exactly one hole's score more thanTotal, that hole is treated as not played. If no single hole explains the gap (or several could), nothing is dropped and the gap is reported instead.get_roundshowsscratched_holesandtotal_gap,export_statuscounts the affected rounds, and the website notes them.Hole numbers are only compared within one course layout.
The UDisc export has no throws, putts or penalty columns, so none are reported.
TechDisc throws are practice data and are not linked to rounds (the export has no course or round column). Throw times come from
timeSecondsand are shown in this computer's local time zone.When both exports share a folder, each loader ignores the other's files by header, and
export_statusonly looks at scorecard files.
Development
.venv\Scripts\python -m pytest -q
.venv\Scripts\ruff check .
cd site && npm test && npm run typecheck # the website, needs Node.jsTests use made-up data in tests/fixtures/ (sample.csv and throws.csv, plus larger generated
synthetic_*.csv). Never commit your real export: *.csv is git-ignored except for tests/fixtures/.
.github/workflows/ci.yml runs all of the above on every push and pull request.
License
MIT, see LICENSE.
Available Tools
13 toolscompare_seasonsBRead-onlyIdempotent
Compare two calendar years: rounds, average score vs par, best/worst round and the change.
Inputs: year_a (required), year_b (required), course.
course_statsARead-onlyIdempotent
Average, best and worst rounds, per-layout stats and the 3 best and worst holes for a course (substring match).
include_holes adds the average for every hole; that list is long for multi-layout courses.
Inputs: course (required), layout, include_holes.
export_statusBRead-onlyIdempotent
How fresh the data is: newest round, newest export file, and a stale warning.
Inputs: no named parameters.
get_roundARead-onlyIdempotent
Hole-by-hole scores for one round. round_id is 'YYYY-MM-DD HHMM' from list_rounds, or a date if unambiguous.
Inputs: round_id (required).
hole_problem_reportBRead-onlyIdempotent
Holes that have cost the most total strokes over par, with average and blow-up counts.
A blow-up is a hole played 2 or more strokes over par (double bogey or worse).
Inputs: top_n, course, layout, min_rounds. Returns: result
list_coursesCRead-onlyIdempotent
Courses played, with rounds played, layouts and first/last played dates.
Inputs: no named parameters. Returns: result
list_roundsARead-onlyIdempotent
List rounds, newest first. start/end are YYYY-MM-DD (inclusive); course matches by substring.
Inputs: end, limit, start, course. Returns: result
list_throwsBRead-onlyIdempotent
List TechDisc throws, newest first. primary_type is Backhand/Forehand; throw_type is e.g. Hyzer/Flat.
Inputs: end, limit, start, throw_type, +1 more. Returns: result
scoring_distributionBRead-onlyIdempotent
Share of holes scored eagle or better, birdie, par, bogey, double and triple or worse.
start/end are YYYY-MM-DD (inclusive). group_by is omitted for one overall row, or 'month' or 'season'.
Inputs: end, start, course, layout, +1 more. Returns: result
scoring_trendsBRead-onlyIdempotent
Average score vs par over time, grouped by 'month' or 'season' (calendar year); complete rounds only.
Inputs: course, layout, group_by. Returns: result
throw_export_statusARead-onlyIdempotent
How fresh the TechDisc data is: throw count, first/last throw, export file age and a stale warning.
Inputs: no named parameters.
throw_statsCRead-onlyIdempotent
TechDisc averages and maxima (speed, distance, spin, angles), overall and by throw style.
Inputs: end, start.
throw_trendsBRead-onlyIdempotent
TechDisc speed, distance and spin over time, grouped by 'month' or 'season' (calendar year).
Inputs: group_by, throw_type, primary_type. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 13 tools
Tools are mostly distinct: list/get for rounds, stats/trends for scoring, and separate throw tools. Minor overlap between course_stats (includes best/worst holes) and hole_problem_report, and between scoring_trends and compare_seasons, but descriptions clarify.
All names use snake_case, but the pattern mixes verb-first (list_rounds, get_round, compare_seasons) with noun-phrase (course_stats, scoring_distribution, throw_export_status). This is a mixed convention, though still readable.
13 tools are well within the typical 3-15 range and cover both round analysis and TechDisc throw analysis without obvious bloat.
The tool set covers round listing/retrieval, course and hole statistics, scoring distributions/trends, season comparison, and TechDisc throw stats/trends, plus export freshness for both data sources. Minor gaps might include a dedicated tool for per-round summary or course comparison, but the surface is largely complete for analysis.
Maintenance
Related MCP Connectors
Read-only PDGA disc golf data — players, events, courses and season statistics.
Log golf rounds in ChatGPT/Claude; scorecards, trends, unofficial WHS math.
Ask questions about a CSV in plain English and get answers, charts and cleaned data back.
Search disc golf courses worldwide, find nearby courses, and retrieve course details and updates.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables querying and analyzing personal Strava activity data through natural language, including activities, segments, gear, and training trends.-
- FlicenseNot gradedqualityDmaintenanceEnables analysis of FitNotes workout data through an LLM, providing insights on progress, muscle groups, and personalized recommendations.4-
- FlicenseAqualityDmaintenanceEnables querying and analyzing sports activities from Strava and Garmin Connect, including sleep and recovery data.10-
- AlicenseNot gradedqualityCmaintenanceEnables natural-language queries about disk usage by driving WizTree scans and analyzing cached CSV snapshots. Users can find large files, duplicate data, folder breakdowns, and more without repeatedly rescanning the disk.19MIT