trackman-mcp 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. trackman_mcp-0.1.0/.claude-plugin/marketplace.json +15 -0
  2. trackman_mcp-0.1.0/.claude-plugin/plugin.json +10 -0
  3. trackman_mcp-0.1.0/.env.example +17 -0
  4. trackman_mcp-0.1.0/.github/workflows/ci.yml +45 -0
  5. trackman_mcp-0.1.0/.github/workflows/publish.yml +55 -0
  6. trackman_mcp-0.1.0/.gitignore +38 -0
  7. trackman_mcp-0.1.0/.mcp.json +9 -0
  8. trackman_mcp-0.1.0/CLAUDE.md +249 -0
  9. trackman_mcp-0.1.0/LICENSE +21 -0
  10. trackman_mcp-0.1.0/PKG-INFO +186 -0
  11. trackman_mcp-0.1.0/PUBLISHING.md +71 -0
  12. trackman_mcp-0.1.0/README.md +155 -0
  13. trackman_mcp-0.1.0/docs/trackman-api.md +256 -0
  14. trackman_mcp-0.1.0/pyproject.toml +70 -0
  15. trackman_mcp-0.1.0/scripts/check-visualization.py +72 -0
  16. trackman_mcp-0.1.0/scripts/eval_analyzer.py +134 -0
  17. trackman_mcp-0.1.0/scripts/install-refresh-schedule.sh +114 -0
  18. trackman_mcp-0.1.0/scripts/refresh-token.sh +28 -0
  19. trackman_mcp-0.1.0/scripts/validate.py +149 -0
  20. trackman_mcp-0.1.0/scripts/visualize.py +17 -0
  21. trackman_mcp-0.1.0/server.json +27 -0
  22. trackman_mcp-0.1.0/skills/drill-library/SKILL.md +64 -0
  23. trackman_mcp-0.1.0/skills/golf-coaching/SKILL.md +143 -0
  24. trackman_mcp-0.1.0/skills/trackman-api-discovery/SKILL.md +69 -0
  25. trackman_mcp-0.1.0/skills/trackman-session-analyzer/SKILL.md +96 -0
  26. trackman_mcp-0.1.0/skills/trackman-stats-analysis/SKILL.md +59 -0
  27. trackman_mcp-0.1.0/skills/trackman-visualizer/SKILL.md +85 -0
  28. trackman_mcp-0.1.0/src/trackman_mcp/__init__.py +7 -0
  29. trackman_mcp-0.1.0/src/trackman_mcp/analysis.py +501 -0
  30. trackman_mcp-0.1.0/src/trackman_mcp/client.py +129 -0
  31. trackman_mcp-0.1.0/src/trackman_mcp/config.py +103 -0
  32. trackman_mcp-0.1.0/src/trackman_mcp/login.py +156 -0
  33. trackman_mcp-0.1.0/src/trackman_mcp/queries.py +277 -0
  34. trackman_mcp-0.1.0/src/trackman_mcp/server.py +579 -0
  35. trackman_mcp-0.1.0/src/trackman_mcp/session_store.py +75 -0
  36. trackman_mcp-0.1.0/src/trackman_mcp/storage.py +86 -0
  37. trackman_mcp-0.1.0/src/trackman_mcp/token_store.py +88 -0
  38. trackman_mcp-0.1.0/src/trackman_mcp/training_store.py +99 -0
  39. trackman_mcp-0.1.0/src/trackman_mcp/visualize.py +363 -0
  40. trackman_mcp-0.1.0/tests/test_analysis.py +192 -0
  41. trackman_mcp-0.1.0/tests/test_client.py +105 -0
  42. trackman_mcp-0.1.0/tests/test_config.py +80 -0
  43. trackman_mcp-0.1.0/tests/test_login_recovery.py +87 -0
  44. trackman_mcp-0.1.0/tests/test_login_stdout.py +37 -0
  45. trackman_mcp-0.1.0/tests/test_queries.py +23 -0
  46. trackman_mcp-0.1.0/tests/test_server.py +188 -0
  47. trackman_mcp-0.1.0/tests/test_session_store.py +93 -0
  48. trackman_mcp-0.1.0/tests/test_storage.py +57 -0
  49. trackman_mcp-0.1.0/tests/test_token_store.py +60 -0
  50. trackman_mcp-0.1.0/tests/test_training_store.py +82 -0
  51. trackman_mcp-0.1.0/tests/test_verify.py +71 -0
  52. trackman_mcp-0.1.0/tests/test_verify_tool.py +105 -0
  53. trackman_mcp-0.1.0/tests/test_visualize.py +73 -0
  54. trackman_mcp-0.1.0/uv.lock +1835 -0
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "trackman-golf",
3
+ "owner": { "name": "Bjorn Jonsson", "email": "bjorn@avo.sh" },
4
+ "metadata": {
5
+ "description": "Single-plugin marketplace for the Trackman Golf MCP + coaching skills.",
6
+ "version": "0.1.0"
7
+ },
8
+ "plugins": [
9
+ {
10
+ "name": "trackman-golf",
11
+ "source": "./",
12
+ "description": "Trackman Golf MCP server (stats as MCP tools) plus the coaching skills."
13
+ }
14
+ ]
15
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "trackman-golf",
3
+ "description": "Connect to Trackman Golf with your own login, expose your stats as MCP tools, and coach from them with bundled skills.",
4
+ "version": "0.1.0",
5
+ "author": { "name": "Bjorn Jonsson", "email": "bjorn@avo.sh" },
6
+ "homepage": "https://github.com/bjornj12/trackman-mcp-client",
7
+ "repository": "https://github.com/bjornj12/trackman-mcp-client",
8
+ "license": "MIT",
9
+ "keywords": ["golf", "trackman", "coaching", "mcp"]
10
+ }
@@ -0,0 +1,17 @@
1
+ # Trackman Golf MCP — environment
2
+
3
+ # REQUIRED: a Bearer access token captured from an authenticated portal session.
4
+ # How to get it:
5
+ # 1. Log in at https://portal.trackmangolf.com
6
+ # 2. Open DevTools -> Network, filter for "graphql"
7
+ # 3. Click a request to api.trackmangolf.com/graphql
8
+ # 4. Copy the Authorization header value (the part after "Bearer ")
9
+ # Paste it here (with or without the leading "Bearer ").
10
+ # Tokens last ~7 days — re-capture when tools return a 401.
11
+ TRACKMAN_TOKEN=
12
+
13
+ # OPTIONAL: override the GraphQL endpoint (defaults to the production API).
14
+ # TRACKMAN_GRAPHQL_ENDPOINT=https://api.trackmangolf.com/graphql
15
+
16
+ # OPTIONAL: request timeout in seconds (default 30).
17
+ # TRACKMAN_TIMEOUT_SECONDS=30
@@ -0,0 +1,45 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python-version: ["3.11", "3.12"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Install uv
19
+ uses: astral-sh/setup-uv@v5
20
+ with:
21
+ enable-cache: true
22
+
23
+ - name: Set up Python ${{ matrix.python-version }}
24
+ run: uv python install ${{ matrix.python-version }}
25
+
26
+ - name: Install dependencies
27
+ run: uv sync --extra dev --python ${{ matrix.python-version }}
28
+
29
+ - name: Lint (ruff)
30
+ run: uv run ruff check src tests
31
+
32
+ - name: Type-check (mypy)
33
+ run: uv run mypy
34
+
35
+ - name: Test (pytest)
36
+ run: uv run pytest -q
37
+
38
+ build:
39
+ runs-on: ubuntu-latest
40
+ steps:
41
+ - uses: actions/checkout@v4
42
+ - name: Install uv
43
+ uses: astral-sh/setup-uv@v5
44
+ - name: Build sdist + wheel
45
+ run: uv build
@@ -0,0 +1,55 @@
1
+ name: Publish to PyPI
2
+
3
+ # Publishes to PyPI on a version tag (e.g. `v0.1.0`) using PyPI Trusted
4
+ # Publishing (OIDC) — no API token is stored anywhere. See PUBLISHING.md for the
5
+ # one-time PyPI-side setup that authorizes this exact workflow.
6
+
7
+ on:
8
+ push:
9
+ tags: ["v*"]
10
+
11
+ jobs:
12
+ build:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Install uv
18
+ uses: astral-sh/setup-uv@v5
19
+
20
+ - name: Verify the tag matches the package version
21
+ run: |
22
+ TAG="${GITHUB_REF_NAME#v}"
23
+ VER=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
24
+ echo "tag=$TAG pyproject=$VER"
25
+ test "$TAG" = "$VER" || { echo "::error::tag v$TAG does not match pyproject version $VER"; exit 1; }
26
+
27
+ - name: Lint, type-check, test (gate the release)
28
+ run: |
29
+ uv sync --extra dev
30
+ uv run ruff check src tests
31
+ uv run mypy
32
+ uv run pytest -q
33
+
34
+ - name: Build sdist + wheel
35
+ run: uv build
36
+
37
+ - uses: actions/upload-artifact@v4
38
+ with:
39
+ name: dist
40
+ path: dist/
41
+
42
+ publish:
43
+ needs: build
44
+ runs-on: ubuntu-latest
45
+ environment: pypi
46
+ permissions:
47
+ id-token: write # required for OIDC trusted publishing
48
+ steps:
49
+ - uses: actions/download-artifact@v4
50
+ with:
51
+ name: dist
52
+ path: dist/
53
+
54
+ - name: Publish to PyPI
55
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,38 @@
1
+ # Secrets & credentials — NEVER commit
2
+ .env
3
+ .env.*
4
+ !.env.example
5
+ *.token
6
+ *token*.json
7
+ *credentials*.json
8
+
9
+ # Local session/auth cache
10
+ .cache/
11
+ .sessions/
12
+
13
+ # Python
14
+ __pycache__/
15
+ *.py[cod]
16
+ .venv/
17
+ venv/
18
+ *.egg-info/
19
+ .pytest_cache/
20
+ .mypy_cache/
21
+ .ruff_cache/
22
+
23
+ # Tooling
24
+ .uv/
25
+ dist/
26
+ build/
27
+
28
+ # OS / editor
29
+ .DS_Store
30
+ .idea/
31
+ .vscode/
32
+
33
+ # uv.lock IS committed for reproducible installs (do not ignore).
34
+
35
+ # Generated visualizations (contain personal data)
36
+ viz/
37
+ *-viz.html
38
+ *.viz.html
@@ -0,0 +1,9 @@
1
+ {
2
+ "mcpServers": {
3
+ "trackman-golf": {
4
+ "command": "uvx",
5
+ "args": ["--from", "${CLAUDE_PLUGIN_ROOT}", "trackman-mcp"],
6
+ "env": { "TRACKMAN_TOKEN": "${TRACKMAN_TOKEN}" }
7
+ }
8
+ }
9
+ }
@@ -0,0 +1,249 @@
1
+ # Trackman Golf MCP
2
+
3
+ ## Project Overview
4
+
5
+ **Name**: Trackman Golf MCP (working name)
6
+
7
+ **Purpose**: A Model Context Protocol (MCP) server that connects to Trackman's
8
+ golf platform using the user's own login, fetches their stats (course rounds,
9
+ practice sessions, shot-level launch-monitor data, club gapping, handicap), and
10
+ exposes them as MCP tools. On top of that data, a set of Claude **skills** act
11
+ as the user's golf coach — diagnosing weaknesses and giving actionable,
12
+ specific practice plans with example drills and YouTube links to follow.
13
+
14
+ **Who it's for**: The individual golfer who already practices on Trackman bays /
15
+ ranges or plays Trackman-enabled courses, and wants their data turned into a
16
+ concrete "what should I work on next" plan.
17
+
18
+ ---
19
+
20
+ ## The Core Boundary (read this first)
21
+
22
+ There is a **hard separation of concerns**. Respect it in every change:
23
+
24
+ - **The MCP server only fetches and returns raw data.** Authentication,
25
+ HTTP calls to Trackman, and shaping responses into clean JSON. It contains
26
+ **no coaching opinions, no drill recommendations, no analysis verdicts.**
27
+ - **The Claude skills do all the thinking.** They call the MCP tools to get
28
+ data, then diagnose, plan, and coach in prompt/markdown.
29
+
30
+ Why: coaching logic should be tunable by editing markdown, not redeploying a
31
+ server. Keep judgment out of the server and data out of the skills.
32
+
33
+ If you find yourself adding "recommend a drill" logic to the MCP, stop — that
34
+ belongs in a skill. If you find yourself hardcoding shot data in a skill, stop —
35
+ that belongs behind an MCP tool.
36
+
37
+ ---
38
+
39
+ ## Status & Phases
40
+
41
+ This repo currently contains **documentation and skills only** — no server code
42
+ yet. Build in this order:
43
+
44
+ - **Phase 0 — API discovery (do this first).** Trackman has no public golf API.
45
+ Before writing a single tool, discover the real endpoints and auth flow the
46
+ web portal uses, and write them down. Use the `trackman-api-discovery` skill.
47
+ Output: `docs/trackman-api.md` filled in.
48
+ - **Phase 1 — MCP server.** Implement the tools below against the discovered
49
+ API. Python + FastMCP.
50
+ - **Phase 2 — Coaching skills.** Wire `trackman-stats-analysis` and
51
+ `golf-coaching` to real tool output; grow the `drill-library`.
52
+
53
+ Do not skip Phase 0. Tool names and shapes below are **provisional** and will be
54
+ corrected by what discovery finds.
55
+
56
+ ---
57
+
58
+ ## Technology Stack
59
+
60
+ - **Runtime**: Python 3.12+
61
+ - **MCP framework**: [FastMCP](https://github.com/jlowin/fastmcp) / the official
62
+ `mcp` Python SDK
63
+ - **HTTP client**: `httpx` (async)
64
+ - **Auth/session**: `httpx` cookie/token session; tokens stored locally only
65
+ - **Data shaping**: plain dicts / `pydantic` models; `pandas` is allowed in
66
+ *skills'* helper scripts for analysis, not required in the server
67
+ - **Package/deps**: `uv` (preferred) or `pip` + `pyproject.toml`
68
+ - **Tests**: `pytest`; record real API responses as fixtures (with secrets
69
+ scrubbed) and test tools against those.
70
+
71
+ ---
72
+
73
+ ## MCP Tools (confirmed against the real API — see `docs/trackman-api.md`)
74
+
75
+ All tools return **raw, structured data only**. No prose, no advice. Every tool
76
+ calls the single GraphQL endpoint `POST https://api.trackmangolf.com/graphql`
77
+ under the signed-in user's `me` root.
78
+
79
+ | Tool | Backing (under `me`) | Returns |
80
+ |------|----------------------|---------|
81
+ | `authenticate` | OIDC token capture | Validates the current token; reports who you're signed in as (or that the session expired). Never echoes the token. |
82
+ | `login` | Browser capture | (Re)authenticate. Tries a silent refresh first; if the saved session expired, opens a browser window to sign in. The friendly recovery path when tools report an expired session. |
83
+ | `get_profile` | `profile` + `hcp` | Identity + current **handicap** (`hcp.currentHcp`). |
84
+ | `get_handicap` | `hcp.playerHistory` | Handicap record history (differentials, trend). |
85
+ | `list_sessions` | `activities(kinds,timeFrom,timeTo,skip,take)` | Practice + course activities (id, time, kind, summary). |
86
+ | `get_session` | `node(id)` / `activities` | One activity in full: range strokes or round detail. |
87
+ | `get_course_rounds` | `scorecards(skip,take,completed)` | Scorecards: per-hole scores, FIR/GIR, putts, `stat`. |
88
+ | `get_club_stats` | `equipment.clubs.findMyDistance` | Per-club **gapping**: carry/total, std-dev, dispersion. |
89
+ | `get_shot_data` | `*.measurement` (`Measurement`) | Shot launch metrics: ball/club speed, smash, launch, spin, carry, side, curve, landing angle (~80 fields). |
90
+ | `get_activity_summary` | `activitySummary(timeFrom,timeTo)` | Counts per activity kind over a window. |
91
+
92
+ ### Session-analysis tools (local store, deterministic analytics)
93
+
94
+ These persist and serve a per-session *analysis*. The analytics are
95
+ **deterministic** (in `analysis.py`) — classification and measurement, not
96
+ coaching. Coaching narrative still lives in the skills. The store is JSON at
97
+ `~/.trackman-mcp/session-analyses.json`, capped at the **last 30**, latest first.
98
+
99
+ | Tool | Does |
100
+ |------|------|
101
+ | `analyze_and_store_session(activity_id)` | Fetch a session, classify (warm-up vs serious practice vs game), compute metrics + course difficulty, normalize vs previously stored sessions, flag used-vs-available clubs, store, return the record. |
102
+ | `list_session_analyses()` | Index of stored analyses (id, time, kind, category, seriousness, summary), latest first. |
103
+ | `get_session_analysis(activity_id)` | One full stored analysis record. |
104
+
105
+ Classification (see `analysis.py`): a session is a **warm-up** (not an
106
+ improvement attempt) if under ~8 strokes or ~5 minutes — even for an otherwise
107
+ "serious" kind; **serious practice** if it has real volume/duration/club variety
108
+ or is a focused kind (shot analysis, find-my-distance, sim/virtual-range, etc.);
109
+ **game** for played rounds. Normalization is always against sessions
110
+ *chronologically before* the one analyzed. Units are metric (m/s, meters).
111
+
112
+ ### Training-plan tools (the coach's memory)
113
+
114
+ The coach saves prescribed practice sessions so they can be recalled later
115
+ ("what's today's training?"). Store is JSON at `~/.trackman-mcp/training-plans.json`
116
+ (`training_store.py`), an ordered queue capped at the most recent 50.
117
+
118
+ | Tool | Does |
119
+ |------|------|
120
+ | `save_training_plan(plan)` | Persist a prescribed plan (title, focus, diagnosis, blocks, targets) to the pending queue. |
121
+ | `get_next_training()` | Return the next pending plan — the answer to "what's today's training?". |
122
+ | `list_training_plans(status?)` | List plans (oldest→newest), optional `pending`/`done` filter. |
123
+ | `mark_training_done(plan_id, result_session_id?)` | Complete a plan; the next pending one becomes current. |
124
+ | `verify_training_progress(plan_id, activity_id?)` | Grade a recent session's real shot metrics against the plan's structured `target_specs` (e.g. driver `clubPath` between -1 and +2). Returns per-target session-mean vs target, `all_met`, and a recommendation. |
125
+
126
+ Plans carry **`target_specs`** — machine-readable targets (`{metric, club?, op,
127
+ value|low/high}`, ops `< <= > >= between abs< abs<=`) graded deterministically by
128
+ `analysis.verify_targets` against a session's `Measurement` fields (queried via
129
+ `SESSION_MEASUREMENTS`, which includes face/path/spin).
130
+
131
+ `golf-coaching` writes here (Prescribe → `save_training_plan` with `target_specs`)
132
+ and reads here (Recall → `get_next_training` → `verify_training_progress`, then
133
+ `mark_training_done` once every target is met).
134
+
135
+ **Auth reality**: the web portal uses a *confidential* OIDC client (backend-for-
136
+ frontend), so the MCP cannot run the OAuth exchange itself. It authenticates with
137
+ a **Bearer access token captured from an authenticated portal session**, attached
138
+ as `Authorization: Bearer …`. Tokens last ~7 days (observed `iat`→`exp` =
139
+ 604800s); on `401` the tool returns a clear "re-capture token" error.
140
+
141
+ **Recovery when expired**: data tools auto-retry once after a silent headless
142
+ refresh (`_try_silent_refresh`), so a stale 7-day token renews invisibly while the
143
+ browser session is still valid. When the browser session itself expires, tools
144
+ return a clear "session expired — use the `login` tool" message, and the `login`
145
+ tool opens a sign-in window (falling back from a fast silent attempt).
146
+
147
+ **Getting the token** — two paths (`Config.from_env`: `TRACKMAN_TOKEN` env wins,
148
+ else the cached token):
149
+ - **Browser login (recommended)**: `trackman-mcp login` opens an isolated
150
+ Playwright browser; the user signs in once; the token is captured from the
151
+ GraphQL traffic and cached at `~/.trackman-mcp/token.json` (mode `0600`). The
152
+ browser profile persists the session, so `trackman-mcp login --headless`
153
+ refreshes silently with no re-login (cron-friendly). Code: `login.py`,
154
+ `token_store.py`. Playwright is the optional `[login]` extra.
155
+ - **Manual**: set `TRACKMAN_TOKEN` from a captured portal session (`.env.example`).
156
+
157
+ Full detail and example GraphQL queries live in `docs/trackman-api.md`.
158
+
159
+ ---
160
+
161
+ ## Authentication & Secrets — Rules
162
+
163
+ Treat the user's Trackman login as sensitive. **Non-negotiable:**
164
+
165
+ - **Never commit credentials, tokens, cookies, or session dumps.** They go in
166
+ `.env` (gitignored) or the OS keychain — never in source or fixtures.
167
+ - The MCP reads credentials from environment variables only
168
+ (`TRACKMAN_USERNAME`, `TRACKMAN_PASSWORD`, or a captured `TRACKMAN_TOKEN`).
169
+ See `.env.example` once Phase 1 starts.
170
+ - **Do not return raw auth material to the model.** Tools may say "authenticated
171
+ as <name>" but must not echo passwords or bearer tokens.
172
+ - **Scrub fixtures.** Any recorded API response saved for tests must have
173
+ tokens, cookies, emails, and player IDs redacted or faked.
174
+ - Cache sessions locally under a gitignored path (e.g. `.cache/`), not in the
175
+ repo tree.
176
+ - This MCP is for a user accessing **their own** Trackman account. Don't build
177
+ anything that scrapes or accesses other users' data.
178
+
179
+ ---
180
+
181
+ ## Project Structure (target, once Phase 1 begins)
182
+
183
+ ```
184
+ trackman-mcp-client/
185
+ ├── CLAUDE.md # this file
186
+ ├── README.md
187
+ ├── pyproject.toml
188
+ ├── .env.example
189
+ ├── .gitignore
190
+ ├── docs/
191
+ │ └── trackman-api.md # discovered endpoints (Phase 0 output)
192
+ ├── src/
193
+ │ └── trackman_mcp/
194
+ │ ├── __init__.py
195
+ │ ├── server.py # FastMCP app + tool registration
196
+ │ ├── client.py # Trackman HTTP client + auth/session
197
+ │ ├── tools/ # one module per tool group
198
+ │ └── models.py # pydantic response models
199
+ ├── tests/
200
+ │ ├── fixtures/ # scrubbed recorded responses
201
+ │ └── test_tools.py
202
+ ├── .claude-plugin/ # Claude Code plugin + marketplace manifests
203
+ ├── .mcp.json # MCP server declaration (for the plugin)
204
+ ├── server.json # MCP Registry manifest
205
+ └── skills/ # coaching brain (see below); plugin-root layout
206
+ ```
207
+
208
+ ---
209
+
210
+ ## Skills (the coaching brain)
211
+
212
+ Skills live in `skills/` (the Claude Code plugin-root layout, so they ship with
213
+ the plugin). Each has a `SKILL.md`.
214
+
215
+ - **`trackman-api-discovery`** — Phase 0. Reverse-engineer the portal's auth +
216
+ data endpoints via the browser network panel; write them into
217
+ `docs/trackman-api.md`.
218
+ - **`trackman-stats-analysis`** — Pull stats through the MCP and diagnose weak
219
+ areas (dispersion, gapping, scoring trends, handicap movement). Analysis only.
220
+ - **`golf-coaching`** — Turn the diagnosis into specific, actionable practice:
221
+ an example session, drills, and YouTube links. The coach persona.
222
+ - **`drill-library`** — Curated drills + vetted YouTube links, plus the
223
+ procedure for live web-searching fresh videos matched to a weakness.
224
+ - **`trackman-session-analyzer`** — Ingests recent sessions, stores a per-session
225
+ analysis (last 30) via the MCP, and returns a normalized summary of the latest
226
+ session. **Context-forked / data-collection skill: must run in a subagent,
227
+ never on the main thread.**
228
+
229
+ Typical flow: `authenticate` → `trackman-stats-analysis` (diagnose) →
230
+ `golf-coaching` (prescribe, pulling from `drill-library`). For per-session
231
+ ingest + a normalized latest-session report, dispatch `trackman-session-analyzer`
232
+ as a subagent.
233
+
234
+ ---
235
+
236
+ ## Conventions
237
+
238
+ - Keep tools small and single-purpose; one concern per module.
239
+ - Tools fail loudly with clear errors (auth expired, endpoint changed) rather
240
+ than returning empty success.
241
+ - Prefer async `httpx`; don't block the event loop.
242
+ - When the API shape is uncertain, write a fixture-backed test from a real
243
+ (scrubbed) response so regressions are caught when Trackman changes things.
244
+ - Coaching is **specific**: "10 balls, 7-iron, alternate target 140/160y, log
245
+ carry dispersion" — never "practice your irons."
246
+
247
+ ---
248
+
249
+ *Last updated: 2026-06-27 · Version 0.1.0 (scaffolding)*
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bjorn Jonsson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,186 @@
1
+ Metadata-Version: 2.4
2
+ Name: trackman-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP server that fetches a user's Trackman Golf stats (handicap, rounds, practice, shots, club gapping).
5
+ Project-URL: Homepage, https://github.com/bjornj12/trackman-mcp-client
6
+ Project-URL: Repository, https://github.com/bjornj12/trackman-mcp-client
7
+ Project-URL: Issues, https://github.com/bjornj12/trackman-mcp-client/issues
8
+ Author-email: Bjorn Jonsson <bjorn@avo.sh>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: claude,golf,llm,mcp,model-context-protocol,trackman
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: End Users/Desktop
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Games/Entertainment
19
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: fastmcp>=2.0.0
22
+ Requires-Dist: httpx>=0.27
23
+ Provides-Extra: dev
24
+ Requires-Dist: mypy>=1.10; extra == 'dev'
25
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
26
+ Requires-Dist: pytest>=8.0; extra == 'dev'
27
+ Requires-Dist: ruff>=0.6; extra == 'dev'
28
+ Provides-Extra: login
29
+ Requires-Dist: playwright>=1.40; extra == 'login'
30
+ Description-Content-Type: text/markdown
31
+
32
+ <!-- mcp-name: io.github.bjornj12/trackman-mcp -->
33
+
34
+ # trackman-mcp
35
+
36
+ An MCP server that logs into **Trackman Golf** with your own account and exposes
37
+ your stats — course rounds, practice sessions, shot-level launch-monitor data,
38
+ club gapping, and handicap — as MCP tools. On top of that, a set of Claude
39
+ **skills** act as your golf coach: they diagnose your weaknesses and hand you a
40
+ specific practice plan with drills and YouTube links for your next session.
41
+
42
+ > [!IMPORTANT]
43
+ > **Unofficial.** This project is not affiliated with or endorsed by Trackman.
44
+ > It talks to Trackman's **private** web API using a token from *your own*
45
+ > authenticated session, and automates a browser login on your behalf. This may
46
+ > conflict with Trackman's Terms of Service — use it on your own account, at your
47
+ > own risk. Never use it to access anyone else's data.
48
+
49
+ ## Design boundary
50
+
51
+ - **MCP server** = raw data fetch + auth only. No opinions.
52
+ - **Skills** = all the coaching (analysis, plans, drills).
53
+
54
+ See [`CLAUDE.md`](./CLAUDE.md) for the full architecture and auth/secret rules.
55
+
56
+ ## Install
57
+
58
+ ### Option A — Claude Code plugin (server + skills together)
59
+
60
+ ```text
61
+ /plugin marketplace add bjornj12/trackman-mcp-client
62
+ /plugin install trackman-golf@trackman-golf
63
+ ```
64
+
65
+ This installs the MCP server (run via `uvx`) **and** the six coaching skills.
66
+ Then sign in once (see [Authentication](#authentication)).
67
+
68
+ ### Option B — any MCP client (Claude Desktop, etc.)
69
+
70
+ Once published to PyPI, add this to your client's MCP config (Claude Desktop:
71
+ `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
72
+
73
+ ```json
74
+ {
75
+ "mcpServers": {
76
+ "trackman-golf": {
77
+ "command": "uvx",
78
+ "args": ["trackman-mcp"]
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ No `env` is required: after you run `trackman-mcp login` the server loads the
85
+ cached token from `~/.trackman-mcp/token.json` automatically. (You can instead
86
+ set `TRACKMAN_TOKEN` to override it — see `.env.example`.)
87
+
88
+ To run it straight from a local checkout before publishing, use
89
+ `"command": "uvx", "args": ["--from", "/abs/path/to/trackman-mcp-client", "trackman-mcp"]`.
90
+
91
+ ### Option C — from source (development)
92
+
93
+ ```bash
94
+ uv venv && uv pip install -e '.[login,dev]' # [login] adds Playwright, [dev] adds test/lint tools
95
+ ```
96
+
97
+ ## Authentication
98
+
99
+ ### Browser login (recommended)
100
+
101
+ ```bash
102
+ trackman-mcp login # opens a browser; sign in once with email+password
103
+ ```
104
+
105
+ A browser window opens (an **isolated** profile, not your normal Chrome). Sign
106
+ in once; the MCP captures the access token and caches it at
107
+ `~/.trackman-mcp/token.json` (mode `0600`). The session persists, so to refresh
108
+ later (tokens last ~7 days) just run:
109
+
110
+ ```bash
111
+ trackman-mcp login --headless # silent refresh, no window
112
+ ```
113
+
114
+ If you don't have Google Chrome installed, the browser flow falls back to
115
+ Playwright's bundled Chromium — install it once with `playwright install chromium`.
116
+
117
+ ### Keep it fresh automatically (optional)
118
+
119
+ Schedule the headless refresh so you never think about tokens (twice weekly,
120
+ margin on the ~7-day token). Portable — paths are derived at install time:
121
+
122
+ ```bash
123
+ scripts/install-refresh-schedule.sh dry-run # preview what gets installed
124
+ scripts/install-refresh-schedule.sh # install (macOS launchd / Linux cron)
125
+ scripts/install-refresh-schedule.sh uninstall # remove
126
+ ```
127
+
128
+ Run a headed `trackman-mcp login` **once** first to establish the browser
129
+ session; the schedule then refreshes it silently. Windows: schedule
130
+ `scripts/refresh-token.sh` via Task Scheduler.
131
+
132
+ ### Alternative: paste a token manually
133
+
134
+ Set `TRACKMAN_TOKEN` (it overrides the cache). Get it from
135
+ portal.trackmangolf.com → DevTools → Network → a `graphql` request → the
136
+ `Authorization` header value. See `.env.example`.
137
+
138
+ ## Run
139
+
140
+ ```bash
141
+ trackman-mcp # start the MCP (stdio)
142
+ uv run python scripts/validate.py # validate stats coverage (uses cached token)
143
+ ```
144
+
145
+ ## MCP tools
146
+
147
+ All tools return **raw data only**; the skills interpret it.
148
+
149
+ **Data (read-only):** `authenticate` · `get_profile` · `get_handicap` ·
150
+ `list_sessions` · `get_session` · `get_course_rounds` · `get_club_stats` ·
151
+ `get_shot_data` · `get_activity_summary`
152
+
153
+ **Auth:** `login`
154
+
155
+ **Session analysis (local store, deterministic):** `analyze_and_store_session` ·
156
+ `list_session_analyses` · `get_session_analysis`
157
+
158
+ **Training-plan memory:** `save_training_plan` · `get_next_training` ·
159
+ `list_training_plans` · `mark_training_done` · `verify_training_progress`
160
+
161
+ **Visualization:** `build_visualization` (self-contained animated HTML artifact)
162
+
163
+ See [`CLAUDE.md`](./CLAUDE.md) for the full table and backing GraphQL.
164
+
165
+ ## Skills
166
+
167
+ Bundled under [`skills/`](./skills) (installed automatically with the plugin):
168
+
169
+ - `trackman-api-discovery` — reverse-engineer the portal's API (Phase 0)
170
+ - `trackman-stats-analysis` — diagnose weaknesses from the data
171
+ - `golf-coaching` — turn the diagnosis into an actionable practice plan
172
+ - `drill-library` — curated drills + vetted YouTube links, plus live search
173
+ - `trackman-session-analyzer` — ingest + normalize recent sessions (runs forked)
174
+ - `trackman-visualizer` — animate a diagnosis as an HTML artifact
175
+
176
+ ## Development
177
+
178
+ ```bash
179
+ uv run pytest # tests
180
+ uv run ruff check # lint
181
+ uv run mypy # type-check
182
+ ```
183
+
184
+ ## License
185
+
186
+ [MIT](./LICENSE)