Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ src/
tools/
index.ts # registerTools() — wires every tool onto the server
list-saves.ts # pcm_list_saves
select-save.ts # pcm_select_save
validate-save.ts # pcm_validate_save
get-save-schema.ts # pcm_get_save_schema
get-table-schema.ts # pcm_get_table_schema
get-player-info.ts # pcm_get_player_info
Expand All @@ -53,7 +53,7 @@ All tools are prefixed with `pcm_` and carry `readOnlyHint: true` / `destructive
| Tool | Purpose |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pcm_list_saves` | Discover `.cdb` careers by scanning `Pro Cycling Manager <year>/Cloud` under `%APPDATA%` (**Windows only**). |
| `pcm_select_save` | Validate a `.cdb` path and return metadata. Stateless — the path must be kept in conversation context for later tools. |
| `pcm_validate_save` | Validate a `.cdb` path and return metadata. Stateless — the path must be kept in conversation context for later tools. |
| `pcm_get_save_schema` | List all tables (id + name) in a save via `DB_STRUCTURE`. |
| `pcm_get_table_schema` | Inspect one table: columns (name, type, NOT NULL, PK) + row count. |
| `pcm_get_player_info` | Active human player + team (joins `GAM_user` `game_i_active = 1` with `DYN_team`). |
Expand Down Expand Up @@ -85,7 +85,7 @@ All tools are prefixed with `pcm_` and carry `readOnlyHint: true` / `destructive
- **Tool naming** — all tools are prefixed with `pcm_` (e.g. `pcm_list_saves`) to
avoid conflicts when used alongside other MCP servers.
- **Platform:** auto-discovery is Windows-only. On macOS/Linux (Wine/Proton),
`pcm_list_saves`/`getPcmRoot` throw — pass an absolute `.cdb` path to `pcm_select_save`.
`pcm_list_saves`/`getPcmRoot` throw — pass an absolute `.cdb` path to `pcm_validate_save`.
- **Logging** must go to `stderr` (`console.error`); stdout is the MCP transport.

## README maintenance
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ PCM only ships on Windows, where careers live under:
%APPDATA%/Pro Cycling Manager <year>/Cloud/<profile>/
```

Auto-discovery via `pcm_list_saves` is therefore **Windows only**. On macOS/Linux the saves live inside a Wine/Proton prefix that can't be reliably located — pass an absolute `.cdb` path directly to `pcm_select_save` instead.
Auto-discovery via `pcm_list_saves` is therefore **Windows only**. On macOS/Linux the saves live inside a Wine/Proton prefix that can't be reliably located — pass an absolute `.cdb` path directly to `pcm_validate_save` instead.

## Available tools

Expand All @@ -86,7 +86,7 @@ All tools are prefixed with `pcm_`. Every tool except `pcm_update_save` is read-
| Tool | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **pcm_list_saves** | Discover PCM `.cdb` career save files on this machine by scanning the `Pro Cycling Manager <year>/Cloud` folders under `%APPDATA%` (Windows only). Returns each save's absolute path, file name, last modified date and size (newest first). |
| **pcm_select_save** | Validate that an absolute path points to an existing `.cdb` save file and return its metadata. Stateless — keep the returned path in conversation context to pass to later tools. |
| **pcm_validate_save** | Validate that an absolute path points to an existing `.cdb` save file and return its metadata. Stateless — keep the returned path in conversation context to pass to later tools. |
| **pcm_get_save_schema** | List every table inside a `.cdb` save file, with its ID and name, plus the total table count. |
| **pcm_get_table_schema** | Inspect a single table by name. Returns its columns (name, SQL type, NOT NULL and primary key flags) and its row count. Use `pcm_get_save_schema` first to discover available table names. |
| **pcm_get_player_info** | Get the active human player and their team from a save file. Returns the player login plus team details (name, resolved division name, resolved country name, evaluation and manager). |
Expand All @@ -101,7 +101,7 @@ All tools are prefixed with `pcm_`. Every tool except `pcm_update_save` is read-

Tools are **stateless**: there is no "current save" held by the server. Every tool takes an absolute `savePath`, re-validates it, and re-reads the `.cdb` from disk into a fresh in-memory SQLite database (via [`cdb-converter`](https://www.npmjs.com/package/cdb-converter) + [`sql.js`](https://www.npmjs.com/package/sql.js)) for each call. The source save on disk is never mutated: read tools only ever read it, and `pcm_update_save` writes its changes to a separate output `.cdb`. A typical flow is:

1. `pcm_list_saves` (Windows) or `pcm_select_save` with an explicit path to locate a save.
1. `pcm_list_saves` (Windows) or `pcm_validate_save` with an explicit path to locate a save.
2. `pcm_search_cyclist`, `pcm_get_team_roster`, `pcm_query_save`, … to explore it.
3. `pcm_generate_startlist_xml` to produce a startlist file for a race, or `pcm_update_save` to write an edited copy of the save.

Expand Down
6 changes: 3 additions & 3 deletions src/saves.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const CLOUD_DIR = "Cloud";
* a SteamID64 or a profile name). On macOS/Linux the
* saves live inside a Wine/Proton prefix that we can't reliably locate, so
* auto-discovery is unsupported there — pass an absolute `.cdb` path to
* `select_save` instead.
* `pcm_validate_save` instead.
*
* @throws on non-Windows platforms.
*/
Expand All @@ -44,7 +44,7 @@ export function getPcmRoot(): string {
throw new Error(
"Pro Cycling Manager save auto-discovery is only supported on Windows. " +
"On macOS/Linux the saves live inside a Wine/Proton prefix — " +
"pass an absolute .cdb path to select_save instead.",
"pass an absolute .cdb path to pcm_validate_save instead.",
);
}
return process.env.APPDATA ?? join(homedir(), "AppData", "Roaming");
Expand Down Expand Up @@ -128,7 +128,7 @@ export async function listSaves(
throw new Error(
`No Pro Cycling Manager data found. Expected a "Pro Cycling Manager <year>" folder under: ${root}. ` +
"PCM may not be installed, or its saves live in a custom location — " +
"pass an absolute .cdb path to select_save instead.",
"pass an absolute .cdb path to pcm_validate_save instead.",
);
}

Expand Down
4 changes: 2 additions & 2 deletions src/tools/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerListSaves } from "./list-saves";
import { registerSelectSave } from "./select-save";
import { registerValidateSave } from "./validate-save";
import { registerGetSaveSchema } from "./get-save-schema";
import { registerGetTableSchema } from "./get-table-schema";
import { registerGetPlayerInfo } from "./get-player-info";
Expand All @@ -13,7 +13,7 @@ import { registerSearchTeam } from "./search-team";

export function registerTools(server: McpServer): void {
registerListSaves(server);
registerSelectSave(server);
registerValidateSave(server);
registerGetSaveSchema(server);
registerGetTableSchema(server);
registerGetPlayerInfo(server);
Expand Down
6 changes: 3 additions & 3 deletions src/tools/select-save.ts → src/tools/validate-save.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ const outputSchema = z.object({
sizeBytes: z.number().describe("File size in bytes"),
});

export function registerSelectSave(server: McpServer): void {
export function registerValidateSave(server: McpServer): void {
server.registerTool(
"pcm_select_save",
"pcm_validate_save",
{
title: "Select PCM save",
title: "Validate PCM save",
description:
"Validate that an absolute path points to an existing Pro Cycling Manager `.cdb` save file and return its metadata. Stateless: nothing is stored — keep the returned path in conversation context to pass to later tools.",
inputSchema: {
Expand Down
Loading