tdf-fantasy-mcp 0.0.1

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.
@@ -0,0 +1,17 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: npm
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
7
+ day: monday
8
+ open-pull-requests-limit: 5
9
+ groups:
10
+ mcp-sdk:
11
+ patterns:
12
+ - "@modelcontextprotocol/*"
13
+ typescript-tooling:
14
+ patterns:
15
+ - "typescript"
16
+ - "ts-node"
17
+ - "@types/*"
@@ -0,0 +1,26 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master, main]
6
+ pull_request:
7
+ branches: [master, main]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - uses: actions/setup-node@v4
16
+ with:
17
+ node-version-file: .nvmrc
18
+ cache: npm
19
+
20
+ - run: npm ci
21
+
22
+ - name: Type-check
23
+ run: npx tsc --noEmit
24
+
25
+ - name: Build
26
+ run: npm run build
@@ -0,0 +1,29 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: read
13
+ id-token: write
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - uses: actions/setup-node@v4
18
+ with:
19
+ node-version-file: .nvmrc
20
+ cache: npm
21
+ registry-url: https://registry.npmjs.org
22
+
23
+ - run: npm ci
24
+
25
+ - name: Build
26
+ run: npm run build
27
+
28
+ - name: Publish
29
+ run: npm publish --access public --provenance
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ 20
package/CLAUDE.md ADDED
@@ -0,0 +1,174 @@
1
+ # TdF Fantasy MCP — Claude Context
2
+
3
+ ## Purpose
4
+
5
+ MCP server for the [Tour de France Fantasy game](https://fantasy.letour.fr) by Tissot.
6
+ Exposes game data (riders, teams, stages, standings, leaderboards) and fantasy management
7
+ actions as MCP tools, and provides 18 strategic prompts that combine fantasy game data
8
+ with FirstCycling historical stats for richer analysis.
9
+
10
+ ---
11
+
12
+ ## API
13
+
14
+ **Base URL:** `https://fantasybytissot.letour.fr/v1`
15
+
16
+ **Required header on every request:**
17
+ ```
18
+ X-Access-Key: 630@16.17@
19
+ ```
20
+ Format: `{identity}@{version}@{codeDemo}` — identity `630` is Tour de France, version `16.17`.
21
+
22
+ **Private endpoints additionally require:**
23
+ ```
24
+ Authorization: Token {jwt}
25
+ ```
26
+ The JWT comes from logging in at fantasy.letour.fr. Set it via the `TDF_AUTH_TOKEN` environment variable. If the variable is present, the server includes it on public endpoints too (no harm done).
27
+
28
+ **Off-season behaviour:** Between editions (outside roughly June–July), the game enters maintenance mode. Most `/private/` endpoints return `{"message": "Jeu en cours de mise à jour"}`. The `/public/config.json` endpoint stays live year-round and is the main signal that the API is reachable.
29
+
30
+ ---
31
+
32
+ ## Build & Run
33
+
34
+ **Node 18+ required** — the server uses native `fetch` (not available in Node 16).
35
+
36
+ ```bash
37
+ npm install
38
+ npm run build # tsc → dist/
39
+ npm start # node dist/index.js
40
+ npm run dev # ts-node src/index.ts (no build step)
41
+ ```
42
+
43
+ Entry point: `dist/index.js`
44
+ Transport: stdio (MCP standard)
45
+
46
+ Key dependency: `@modelcontextprotocol/sdk` (requires Node ≥ 18).
47
+
48
+ ---
49
+
50
+ ## Claude Desktop Configuration
51
+
52
+ ```json
53
+ {
54
+ "mcpServers": {
55
+ "tdf-fantasy": {
56
+ "command": "node",
57
+ "args": ["/path/to/tdf-fantasy-mcp/dist/index.js"],
58
+ "env": {
59
+ "TDF_AUTH_TOKEN": "your_jwt_token_here"
60
+ }
61
+ }
62
+ }
63
+ }
64
+ ```
65
+
66
+ Run `nvm use` in the project directory first (picks up `.nvmrc` → Node 24 LTS), then `node` resolves correctly.
67
+
68
+ ---
69
+
70
+ ## Tools (18 total)
71
+
72
+ ### Public — no auth required
73
+
74
+ | Tool | Endpoint | Description |
75
+ |------|----------|-------------|
76
+ | `get_game_config` | `GET /public/config.json` | Full game config: budget, team size, positions, rules |
77
+ | `get_riders` | `GET /public/sportifs` | All riders with names, teams, prices, positions |
78
+ | `get_teams` | `GET /public/clubs` | All professional cycling teams |
79
+
80
+ ### Stages — auth required
81
+
82
+ | Tool | Endpoint | Description |
83
+ |------|----------|-------------|
84
+ | `get_current_stage` | `GET /private/journee` | Current stage info and results |
85
+ | `get_stage` | `GET /private/journee/{n}` | Specific stage by number |
86
+ | `get_stage_calendar` | `GET /private/journeecalendrier/{n}` | Stage schedule/calendar |
87
+
88
+ ### Standings — auth required
89
+
90
+ | Tool | Endpoint | Description |
91
+ |------|----------|-------------|
92
+ | `get_general_standings` | `GET /private/classementgeneral/{groupId}` | Overall fantasy leaderboard |
93
+ | `get_stage_standings` | `GET /private/classementjournee/{groupId}/{n}` | Per-stage leaderboard |
94
+ | `get_player_progression` | `GET /private/progressionjoueur/{groupId}/{n}` | Points across stages |
95
+ | `get_hall_of_fame` | `GET /private/halloffame` | All-time top performers |
96
+
97
+ ### Leagues — auth required
98
+
99
+ | Tool | Endpoint | Description |
100
+ |------|----------|-------------|
101
+ | `get_my_leagues` | `GET /private/mesgroupes` | Leagues the user belongs to |
102
+ | `get_league` | `GET /private/infosgroupe/{groupId}` | Details about a specific league |
103
+ | `search_leagues` | `POST /private/groupessearch` | Search leagues by name or code |
104
+
105
+ ### User & Stats — auth required
106
+
107
+ | Tool | Endpoint | Description |
108
+ |------|----------|-------------|
109
+ | `get_rider_stats` | `POST /private/stats` | Rider stats with optional filters |
110
+ | `get_top_transactions` | `GET /private/toptransaction` | Most bought/sold riders |
111
+ | `get_user_profile` | `GET /private/joueur/{id}` | User fantasy profile |
112
+ | `get_rider_positions` | `GET /private/positions` | Position type definitions |
113
+ | `get_notifications` | `GET /private/notification` | User notifications |
114
+ | `get_user_credits` | `GET /private/usercredits` | Credit balance |
115
+
116
+ ---
117
+
118
+ ## Prompts (18 total)
119
+
120
+ All prompts follow a **two-phase pattern**:
121
+
122
+ 1. **Fantasy data phase** — fetch game data (riders, prices, stages, team) using the tdf-fantasy tools above
123
+ 2. **FirstCycling enrichment phase** — look up relevant riders on the `firstcycling-mcp` server for historical palmares, terrain-specific performance, and recent form before making any recommendation
124
+
125
+ The user is expected to have `firstcycling-mcp` installed alongside this server. Prompts reference "FirstCycling tools" generically so Claude resolves them at runtime.
126
+
127
+ ### Team Building
128
+
129
+ | Prompt | Arguments | Description |
130
+ |--------|-----------|-------------|
131
+ | `strongest_team` | `budget` (req), `strategy` (req: gc/climber/sprinter/puncher/breakaway) | Strongest 8-rider team; validates picks against FirstCycling palmares |
132
+ | `best_value_picks` | `max_price` (req), `specialty` (opt) | Best value under price cap; enriches with FirstCycling form/terrain fit |
133
+ | `design_specialty_team` | `specialty` (req), `budget` (req) | Full team around one specialty; FirstCycling validates track record |
134
+ | `go_all_in_breakaways` | `budget` (req) | Aggressive breakaway stack; FirstCycling verifies actual breakaway history |
135
+
136
+ ### Exchange Planning
137
+
138
+ | Prompt | Arguments | Description |
139
+ |--------|-----------|-------------|
140
+ | `plan_transfers` | `stages_ahead` (req), `exchanges_remaining` (req) | Optimal transfer plan; matches stage terrain against FirstCycling terrain specialties |
141
+ | `who_to_drop_before_stage` | `stage_number` (req) | Drop candidates before a stage |
142
+ | `replacement_for_abandoned_rider` | `abandoned_rider_name` (req), `budget` (req) | Replacement after a DNF |
143
+ | `final_week_strategy` | `exchanges_remaining` (req) | Final week transfer plan |
144
+
145
+ ### Team Analysis
146
+
147
+ | Prompt | Arguments | Description |
148
+ |--------|-----------|-------------|
149
+ | `analyze_team_form` | none | Team form + FirstCycling recent results to separate bad form from bad luck |
150
+ | `who_is_underperforming` | none | Underperformers; FirstCycling distinguishes fading form vs. due a result |
151
+ | `flag_injury_risks` | none | Injury/fatigue flags; FirstCycling recent results spot pre-Tour injury patterns |
152
+ | `close_gap_on_rival` | `rival_name` (req) | Gap-closing strategy; FirstCycling terrain fit compared across both teams |
153
+
154
+ ### Preference-Personalised
155
+
156
+ | Prompt | Arguments | Description |
157
+ |--------|-----------|-------------|
158
+ | `team_around_nationality` | `nationality` (req), `budget` (req) | Nationality-anchored team; FirstCycling ranks candidates by palmares |
159
+ | `team_around_pro_team` | `pro_team_name` (req), `budget` (req) | Pro-team-anchored fantasy team |
160
+ | `find_riders_like_favorite` | `favorite_rider_name` (req) | Similar riders; FirstCycling profile match drives similarity |
161
+ | `specialty_first_team` | `specialty` (req), `budget` (req), `preferred_nationality` (opt) | Specialty-first with optional nationality tie-break |
162
+ | `scout_riders_from_country` | `nationality` (req), `max_price` (req) | Scout a country's riders; FirstCycling palmares is the primary ranking signal |
163
+ | `preferred_riders_value_check` | `rider_names` (req, comma-sep), `budget` (req) | Value check on a personal watchlist |
164
+
165
+ ---
166
+
167
+ ## Rider Position IDs
168
+
169
+ | ID | Label | Description |
170
+ |----|-------|-------------|
171
+ | 43 | Leaders | GC contenders |
172
+ | 44 | Polyvalents | All-rounders |
173
+ | 45 | Grimpeurs | Climbers |
174
+ | 46 | Sprinteurs | Sprinters |
package/INDEX.md ADDED
@@ -0,0 +1,111 @@
1
+ # Codebase Index
2
+
3
+ ## File Tree
4
+
5
+ ```
6
+ tdf-fantasy-mcp/
7
+ ├── src/
8
+ │ └── index.ts # Entire server: API helpers, tools, prompts, MCP wiring
9
+ ├── dist/ # Compiled output (git-ignored); generated by `npm run build`
10
+ │ └── index.js # Entry point for production/Claude Desktop
11
+ ├── CLAUDE.md # Context for Claude Code sessions — API, tools, prompts reference
12
+ ├── INDEX.md # This file
13
+ ├── README.md # User-facing docs: setup, tools table, WVA tribute
14
+ ├── package.json # Dependencies, build/start/dev scripts, engines: node >=18
15
+ ├── package-lock.json # Lockfile
16
+ ├── tsconfig.json # target ES2020, commonjs, outDir dist/, rootDir src/
17
+ └── .gitignore # Ignores node_modules/, dist/, .env
18
+ ```
19
+
20
+ ---
21
+
22
+ ## src/index.ts — Layout
23
+
24
+ The entire implementation lives in one file (~1100 lines). Sections in order:
25
+
26
+ ### Imports (lines 1–9)
27
+ ```
28
+ @modelcontextprotocol/sdk/server/index.js — Server class
29
+ @modelcontextprotocol/sdk/server/stdio.js — StdioServerTransport
30
+ @modelcontextprotocol/sdk/types.js — schemas, Tool, Prompt types
31
+ ```
32
+
33
+ ### API constants & helpers (lines 11–78)
34
+ | Symbol | Purpose |
35
+ |--------|---------|
36
+ | `API_BASE` | `https://fantasybytissot.letour.fr/v1` |
37
+ | `GAME_IDENTITY` / `GAME_VERSION` | `630` / `16.17` — baked into `X-Access-Key` |
38
+ | `ACCESS_KEY` | `630@16.17@` — sent on every request |
39
+ | `getAuthToken()` | Reads `TDF_AUTH_TOKEN` env var |
40
+ | `buildHeaders(requiresAuth)` | Assembles headers; throws if auth required but token absent |
41
+ | `apiGet(path, requiresAuth)` | Authenticated GET → parsed JSON |
42
+ | `apiPost(path, body, requiresAuth)` | Authenticated POST → parsed JSON |
43
+
44
+ ### Tool definitions — `TOOLS` array (lines 80–329)
45
+ 18 `Tool` objects with `name`, `description`, and `inputSchema`. Grouped as:
46
+ - Public: `get_game_config`, `get_riders`, `get_teams`
47
+ - Stages: `get_current_stage`, `get_stage`, `get_stage_calendar`
48
+ - Standings: `get_general_standings`, `get_stage_standings`, `get_player_progression`, `get_hall_of_fame`
49
+ - Leagues: `get_my_leagues`, `get_league`, `search_leagues`
50
+ - User/Stats: `get_rider_stats`, `get_top_transactions`, `get_user_profile`, `get_rider_positions`, `get_notifications`, `get_user_credits`
51
+
52
+ ### Tool handler — `handleTool()` (lines 331–433)
53
+ `switch` on tool name → calls `apiGet` or `apiPost`. One case per tool.
54
+
55
+ ### Prompt definitions — `PROMPTS` array (lines 435–703)
56
+ 18 `Prompt` objects with `name`, `description`, and `arguments` array.
57
+ Each argument has `name`, `description`, and `required: boolean`.
58
+
59
+ ### Prompt helpers (lines 705–715)
60
+ | Symbol | Purpose |
61
+ |--------|---------|
62
+ | `type Args` | `Record<string, string>` — the raw arguments map from MCP |
63
+ | `req(args, key)` | Reads a required arg; throws with a clear message if missing |
64
+ | `opt(args, key)` | Reads an optional arg; returns `undefined` if absent |
65
+
66
+ ### Prompt builder — `buildPromptText()` (lines 717–1023)
67
+ `switch` on prompt name → returns an interpolated string ready to send as a user message.
68
+ Each case calls `req()`/`opt()` for its arguments then builds the prompt text.
69
+ The 11 FirstCycling-enriched prompts explicitly instruct Claude to use FirstCycling tools
70
+ after fetching fantasy data.
71
+
72
+ ### MCP server wiring (lines 1025–1096)
73
+ ```
74
+ new Server({ name, version }, { capabilities: { tools: {}, prompts: {} } })
75
+
76
+ setRequestHandler(ListToolsRequestSchema) → returns TOOLS
77
+ setRequestHandler(ListPromptsRequestSchema) → returns PROMPTS
78
+ setRequestHandler(GetPromptRequestSchema) → calls buildPromptText(), wraps in message
79
+ setRequestHandler(CallToolRequestSchema) → calls handleTool(), wraps result as text
80
+ ```
81
+
82
+ ### Entry point — `main()` (lines 1087–1096)
83
+ Connects a `StdioServerTransport` and logs `"TdF Fantasy MCP server running on stdio"` to stderr.
84
+
85
+ ---
86
+
87
+ ## Gotchas
88
+
89
+ **`get_user_profile` with no `user_id`** calls `/private/joueur/me` — this endpoint may not
90
+ exist on the real API (it was not in the discovered route list). If it 404s, the caller should
91
+ pass an explicit user ID obtained from another endpoint (e.g. league standings).
92
+
93
+ **Off-season responses** — most `/private/` endpoints return a 200 with body
94
+ `{"message": "Jeu en cours de mise à jour"}` rather than a 4xx during maintenance. Tool
95
+ handlers will return this message as valid JSON rather than throwing; callers should check for it.
96
+
97
+ **`get_rider_stats` filter shape** — the POST body is `{ credentials: { positions, clubs, minPrice, maxPrice } }`.
98
+ The API field names are `positions` (not `position_ids`) and `clubs` (not `team_ids`); the
99
+ mapping happens in `handleTool`.
100
+
101
+ **Node 18+ required** — `fetch` is not available in Node 16. The `@modelcontextprotocol/sdk`
102
+ package also declares `engines: { node: ">=18" }`. Use `nvm use 18` or point Claude Desktop's
103
+ `command` at the full Node 18 binary path.
104
+
105
+ **Single-file architecture** — all tools, prompts, and server logic are in `src/index.ts`.
106
+ If the file grows significantly, consider splitting into `src/tools.ts`, `src/prompts.ts`,
107
+ and `src/api.ts` with a thin `src/index.ts` that wires them together.
108
+
109
+ **FirstCycling tool names are not hardcoded** — prompt texts refer to "the FirstCycling tools"
110
+ generically. Claude resolves them at runtime from whatever the `firstcycling-mcp` server
111
+ exposes. This means prompts remain valid even if the firstcycling-mcp tool names change.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ken MacInnis
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.
package/README.md ADDED
@@ -0,0 +1,164 @@
1
+ # TdF Fantasy MCP
2
+
3
+ An MCP (Model Context Protocol) server for the [Tour de France Fantasy game](https://fantasy.letour.fr) by Tissot.
4
+
5
+ ## Overview
6
+
7
+ This server exposes the Tour de France Fantasy game API as MCP tools, letting you query riders, teams, stages, standings, and leaderboards from any MCP-compatible client (Claude Desktop, etc.).
8
+
9
+ The underlying API lives at `https://fantasybytissot.letour.fr/v1`.
10
+
11
+ ## Requirements
12
+
13
+ - **Node.js >= 20** — the server uses native `fetch`, which requires Node 18+ and is stable from Node 20 onward. Check your version with `node --version`; use [nvm](https://github.com/nvm-sh/nvm) to switch if needed.
14
+ - **Recommended companion: [firstcycling-mcp](https://github.com/r-huijts/firstcycling-mcp)** — 11 of the 18 prompts follow a two-phase pattern that enriches fantasy recommendations with FirstCycling historical palmares and terrain data. The tools work without it, but the prompts produce noticeably better output when both servers are connected.
15
+ - **Auth token** — public tools (`get_game_config`, `get_riders`, `get_teams`) work without authentication. All other tools require a valid JWT from the game. See [Authentication](#authentication) below.
16
+
17
+ ## Setup
18
+
19
+ ### Install
20
+
21
+ ```bash
22
+ npm install
23
+ npm run build
24
+ ```
25
+
26
+ ### Authentication
27
+
28
+ Some tools require a valid auth token from the game. To obtain one:
29
+
30
+ 1. Open [fantasy.letour.fr](https://fantasy.letour.fr) in your browser
31
+ 2. Log in to your account
32
+ 3. Open DevTools → Network tab
33
+ 4. Make any in-game action and look for requests to `fantasybytissot.letour.fr/v1/private/`
34
+ 5. Copy the `Authorization` header value (everything after `Token `)
35
+
36
+ Set it as an environment variable before running:
37
+
38
+ ```bash
39
+ export TDF_AUTH_TOKEN="your_jwt_token_here"
40
+ ```
41
+
42
+ Tools that don't require auth (`get_game_config`, `get_riders`, `get_teams`) work without a token.
43
+
44
+ ### Claude Desktop Configuration
45
+
46
+ Add to your `claude_desktop_config.json`:
47
+
48
+ ```json
49
+ {
50
+ "mcpServers": {
51
+ "tdf-fantasy": {
52
+ "command": "node",
53
+ "args": ["/path/to/tdf-fantasy-mcp/dist/index.js"],
54
+ "env": {
55
+ "TDF_AUTH_TOKEN": "your_jwt_token_here"
56
+ }
57
+ }
58
+ }
59
+ }
60
+ ```
61
+
62
+ ## Available Tools
63
+
64
+ ### Public (no auth required)
65
+
66
+ | Tool | Description |
67
+ |------|-------------|
68
+ | `get_game_config` | Full game configuration: budget, team size, rider positions, rules |
69
+ | `get_riders` | All riders with names, teams, prices, positions (active season only) |
70
+ | `get_teams` | All professional cycling teams |
71
+
72
+ ### Private (auth required)
73
+
74
+ | Tool | Description |
75
+ |------|-------------|
76
+ | `get_current_stage` | Current stage info and results |
77
+ | `get_stage` | Specific stage info by number |
78
+ | `get_stage_calendar` | Stage schedule/calendar |
79
+ | `get_general_standings` | Overall fantasy leaderboard for a group |
80
+ | `get_stage_standings` | Per-stage leaderboard for a group |
81
+ | `get_my_leagues` | Your leagues/groups |
82
+ | `get_league` | Details about a specific league |
83
+ | `search_leagues` | Search for leagues by name or code |
84
+ | `get_rider_stats` | Rider statistics with optional filters |
85
+ | `get_top_transactions` | Most bought/sold riders |
86
+ | `get_user_profile` | User fantasy profile and team |
87
+ | `get_rider_positions` | Position type definitions |
88
+ | `get_hall_of_fame` | All-time top performers |
89
+ | `get_player_progression` | Point progression across stages |
90
+ | `get_notifications` | Your game notifications |
91
+ | `get_user_credits` | Your credit balance |
92
+
93
+ ## Rider Positions
94
+
95
+ | ID | Name | Description |
96
+ |----|------|-------------|
97
+ | 43 | Leaders | GC contenders |
98
+ | 44 | Polyvalents | All-rounders |
99
+ | 45 | Grimpeurs | Climbers |
100
+ | 46 | Sprinteurs | Sprinters |
101
+
102
+ ## Notes
103
+
104
+ - The API goes into maintenance mode (`{"message": "Jeu en cours de mise à jour"}`) between editions of the Tour de France. Most private endpoints return this message off-season.
105
+ - The game runs annually during the Tour de France (typically July).
106
+ - Budget per team: 120M (standard), 160M (mercato mode)
107
+ - Team size: 8 riders
108
+
109
+ ## Development
110
+
111
+ ```bash
112
+ npm run dev # Run with ts-node (no build step)
113
+ npm run build # Compile to dist/
114
+ npm start # Run compiled output
115
+ ```
116
+
117
+ ## Prompts
118
+
119
+ The server exposes 18 MCP prompts that clients like Claude Desktop surface as slash commands or quick actions. Select a prompt, fill in the arguments, and Claude will call the relevant tools automatically.
120
+
121
+ Prompts follow a **two-phase pattern**: first fetch live fantasy data (riders, prices, stage calendar, your team) using the tdf-fantasy tools, then enrich the analysis with [firstcycling-mcp](https://github.com/r-huijts/firstcycling-mcp) for historical palmares, recent form, and terrain-specific performance. Having both servers connected gives the best results.
122
+
123
+ **Example — finding the best value climbers under 14 credits:**
124
+ > Invoke `best_value_picks` with `max_price=14`, `specialty=climber`
125
+ >
126
+ > Claude fetches all affordable riders from the fantasy API, checks the upcoming mountain stage profiles, then looks each candidate up on FirstCycling to verify their climbing palmares and current form before ranking them.
127
+
128
+ ### Team Building
129
+
130
+ | Prompt | Arguments | What it does |
131
+ |--------|-----------|--------------|
132
+ | `strongest_team` | `budget` (req), `strategy` (req: gc/climber/sprinter/puncher/breakaway) | Builds the strongest 8-rider team for your budget and strategy; validates picks against FirstCycling palmares |
133
+ | `best_value_picks` | `max_price` (req), `specialty` (opt) | Finds best value-for-money riders under a price cap; enriches with FirstCycling form and terrain fit |
134
+ | `design_specialty_team` | `specialty` (req), `budget` (req) | Designs a full team around one racing specialty; FirstCycling validates each rider's track record |
135
+ | `go_all_in_breakaways` | `budget` (req) | Builds an aggressive breakaway-specialist team; FirstCycling verifies actual breakaway history |
136
+
137
+ ### Exchange Planning
138
+
139
+ | Prompt | Arguments | What it does |
140
+ |--------|-----------|--------------|
141
+ | `plan_transfers` | `stages_ahead` (req), `exchanges_remaining` (req) | Plans optimal transfers for the next N stages; matches terrain against FirstCycling specialties before recommending swaps |
142
+ | `who_to_drop_before_stage` | `stage_number` (req) | Identifies drop candidates before a specific stage with replacement suggestions |
143
+ | `replacement_for_abandoned_rider` | `abandoned_rider_name` (req), `budget` (req) | Finds the best replacement after a key rider abandons |
144
+ | `final_week_strategy` | `exchanges_remaining` (req) | Devises an end-of-Tour transfer plan for the mountain stages, TT, and Champs-Élysées |
145
+
146
+ ### Team Analysis
147
+
148
+ | Prompt | Arguments | What it does |
149
+ |--------|-----------|--------------|
150
+ | `analyze_team_form` | none | Analyses your team's points trajectory; uses FirstCycling recent results to distinguish bad form from bad luck |
151
+ | `who_is_underperforming` | none | Identifies underperformers relative to price; FirstCycling checks whether they're fading or due a result |
152
+ | `flag_injury_risks` | none | Flags injury and fatigue risks; FirstCycling recent results spot pre-Tour patterns |
153
+ | `close_gap_on_rival` | `rival_name` (req) | Plans a gap-closing strategy; compares both teams' riders via FirstCycling terrain fit for remaining stages |
154
+
155
+ ### Preference-Personalised
156
+
157
+ | Prompt | Arguments | What it does |
158
+ |--------|-----------|--------------|
159
+ | `team_around_nationality` | `nationality` (req), `budget` (req) | Builds a team featuring as many riders of a given nationality as possible; ranked by FirstCycling palmares |
160
+ | `team_around_pro_team` | `pro_team_name` (req), `budget` (req) | Anchors a fantasy team around riders from a specific professional team |
161
+ | `find_riders_like_favorite` | `favorite_rider_name` (req) | Finds riders with a similar profile; FirstCycling career match drives the comparison |
162
+ | `specialty_first_team` | `specialty` (req), `budget` (req), `preferred_nationality` (opt) | Builds a specialty-first team with an optional nationality tie-break |
163
+ | `scout_riders_from_country` | `nationality` (req), `max_price` (req) | Scouts a country's riders under a price cap; FirstCycling palmares is the primary ranking signal |
164
+ | `preferred_riders_value_check` | `rider_names` (req, comma-separated), `budget` (req) | Checks whether a personal watchlist fits within budget and at good value |
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}