Skip to main content
Glama
lappi

ttw-mcp

by lappi
README.md
# ttw-mcp

MCP-сервер для доступа к рейтингу настольного тенниса
[r.ttw.ru](https://r.ttw.ru): игроки, матчи, очные противостояния, турниры.

Тонкая прослойка получения данных. Сервер не считает аналитику, не хранит
состояние и не кэширует — всю интерпретацию делает модель над JSON.

## Инструменты

| Инструмент | Что возвращает |
| --- | --- |
| `search_players(name, limit=25)` | игроки по фамилии: id, город, `rating_current`, В-П, `hand` |
| `get_player(player_id, include_matches=True, matches_since="")` | профиль: сводка, стартовый рейтинг, периоды, турниры, матчи с рейтингом игрока на момент матча |
| `get_head_to_head(player_id, opponent_id)` | очное противостояние и все встречи |
| `search_tournaments(name="", date="", date_from="", date_to="", enrich=False)` | турниры по названию, дате или диапазону дат |
| `get_tournament(tournament_id, ratings_at_event=False)` | метаданные, итоговая таблица, турниры серии |
| `get_tournament_matches(tournament_id)` | матчи турнира, восстановленные из профилей участников |
| `get_series(tournament_id, limit=10)` | турниры той же серии из двух источников |

`get_tournament_matches`, `get_series` и `get_tournament` с
`ratings_at_event=True` сначала загружают саму страницу турнира, а потом
профиль каждого участника — запросов на один больше, чем участников, и на
крупном турнире это минуты последовательных обращений. `search_tournaments`
с `enrich=True` стоит по запросу на строку, до двадцати: это потолок на
число обогащаемых строк, а не на размер одного ответа, и при диапазоне дат
склейка за несколько дней может дать больше строк, чем потолок позволяет
обогатить, — тогда инструмент отказывается целиком, а не обогащает часть.

Для `search_tournaments` нужен хотя бы один из аргументов `name`, `date`
или диапазон `date_from`/`date_to` (порознь с `date`); для `search_players`
обязателен `name`. Дата — в формате DD.MM.YYYY.

## Подключение

### Что понадобится

[uv](https://docs.astral.sh/uv/) и Python 3.11 или новее. Клонировать
репозиторий не нужно: `uvx` соберёт сервер прямо из git при первом запуске
и закэширует сборку.

Ключей, токенов и переменных окружения не требуется — источник публичный.

### Команда запуска

Она одна и та же во всех клиентах:

```bash
uvx --from git+https://github.com/lappi/ttw-mcp ttw-mcp
```

В конфигурационных файлах она распадается на `command` и `args`:

```json
"command": "uvx",
"args": ["--from", "git+https://github.com/lappi/ttw-mcp", "ttw-mcp"]
```

Если клиент сообщает, что команда не найдена, подставьте полный путь —
его покажет `which uvx` (обычно `~/.local/bin/uvx`). Клиенты запускают
процесс не из вашей оболочки и переменную `PATH` из `.zshrc` или
`.bashrc` не видят.

### Claude Code

```bash
claude mcp add ttw -- uvx --from git+https://github.com/lappi/ttw-mcp ttw-mcp
```

Двойное тире отделяет опции `claude` от команды сервера: всё, что после
него, передаётся серверу как есть.

Где сохранится настройка, задаёт `--scope`:

| Область | Файл | Видна команде |
| --- | --- | --- |
| `local` (по умолчанию) | `~/.claude.json` | нет |
| `project` | `.mcp.json` в корне проекта | да, через систему контроля версий |
| `user` | `~/.claude.json` | нет |

Проверка: `claude mcp list` в оболочке или `/mcp` внутри сессии.

### Claude Desktop

Настройки → вкладка «Developer» → «Edit Config». Кнопка откроет файл:

- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "ttw": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/lappi/ttw-mcp", "ttw-mcp"]
    }
  }
}
```

После сохранения приложение нужно полностью завершить и запустить заново.
Сервер появится в списке коннекторов рядом с полем ввода.

Если не появился — смотрите журнал: `~/Library/Logs/Claude/mcp*.log` на
macOS, `%APPDATA%\Claude\logs` на Windows. Файл `mcp-server-ttw.log`
содержит всё, что сервер написал в stderr.

### ChatGPT (десктоп), Codex CLI и расширение для IDE

У них общий конфигурационный файл: настроив один раз, вы получаете сервер
во всех трёх.

```bash
codex mcp add ttw -- uvx --from git+https://github.com/lappi/ttw-mcp ttw-mcp
```

Либо вручную, в `~/.codex/config.toml` (или `.codex/config.toml` в
доверенном проекте) — формат здесь TOML, а не JSON:

```toml
[mcp_servers.ttw]
command = "uvx"
args = ["--from", "git+https://github.com/lappi/ttw-mcp", "ttw-mcp"]
```

Проверка: `/mcp` в Codex.

### ChatGPT в браузере — не подключится, и вот почему

Веб-версия не читает локальные конфигурационные файлы: она работает
только с удалёнными MCP-серверами по HTTP, подключаемыми через плагины.
Этот сервер — локальный, он общается по stdio и никуда не выставлен.

Варианта два: пользоваться десктопным приложением (см. раздел выше) либо
поднять HTTP-обёртку над stdio на своём хосте. Второе выходит за рамки
этого репозитория: сервер ходит на сайт, который просит не обходить себя
автоматически, и выставлять его в сеть — отдельное решение с отдельной
ответственностью.

### Gemini CLI

Файл `~/.gemini/settings.json` (глобально) или `.gemini/settings.json`
(в проекте):

```json
{
  "mcpServers": {
    "ttw": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/lappi/ttw-mcp", "ttw-mcp"]
    }
  }
}
```

Проверка: `gemini mcp list` в оболочке или `/mcp` внутри сессии. Добавить
сервер можно и командой `gemini mcp add`.

### Cursor

Файл `~/.cursor/mcp.json` (глобально) или `.cursor/mcp.json` (в проекте),
формат тот же:

```json
{
  "mcpServers": {
    "ttw": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/lappi/ttw-mcp", "ttw-mcp"]
    }
  }
}
```

### VS Code

Здесь формат свой: ключ верхнего уровня называется `servers`, а у сервера
появляется `type`.

Файл `.vscode/mcp.json` в проекте:

```json
{
  "servers": {
    "ttw": {
      "type": "stdio",
      "command": "uvx",
      "args": ["--from", "git+https://github.com/lappi/ttw-mcp", "ttw-mcp"]
    }
  }
}
```

Через палитру команд: **MCP: Add Server** — мастер с выбором области,
**MCP: Open User Configuration** — тот же файл на уровне профиля, для всех
рабочих областей сразу.

### Остальные клиенты

Большинство используют тот же объект `mcpServers`, что Claude Desktop,
Cursor и Gemini CLI, — меняется только путь к файлу:

| Клиент | Как добраться до конфигурации |
| --- | --- |
| Windsurf / Cascade | Меню `...` в панели Cascade → «Open MCP config file». Путь к файлу за последнее время менялся вместе с переездом продукта, поэтому надёжнее открывать его из интерфейса, а не искать вручную |
| Zed | Настройки, блок `context_servers` |
| LM Studio | Настройки → MCP Servers → Add Server: имя, команда, аргументы |
| Cline, Continue, JetBrains и прочие | Ищите в настройках «MCP» и вставляйте тот же объект `mcpServers` |

Клиентов много и конфигурации у них меняются, поэтому если путь из этой
таблицы не совпал — источник истины всегда документация самого клиента, а
не эта. Команда запуска при этом остаётся прежней.

### Проверка без клиента

Если клиент молчит и непонятно, на чьей стороне проблема, сервер можно
опросить напрямую — он ответит на приветствие протокола:

```bash
printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}' \
  | uvx --from git+https://github.com/lappi/ttw-mcp ttw-mcp
```

В ответ придёт одна строка JSON с `serverInfo`, именем `ttw` и версией.
Пришла — сервер исправен, и разбираться надо с настройками клиента. Не
пришла — читайте, что команда написала в stderr.

### Из клона — для разработки

Если вы правите код, запускать надо не из git, а из рабочей копии:

```json
{
  "mcpServers": {
    "ttw": {
      "command": "uv",
      "args": ["--directory", "/путь/к/ttw-mcp", "run", "ttw-mcp"]
    }
  }
}
```

Путь должен быть абсолютным: клиент запускает процесс из своего рабочего
каталога, а не из вашего.

## Ограничения источника

- История не ограничена окном: замерено от 19 до 94 недельных периодов у
  разных игроков, самый ранний — октябрь 2023 года. Глубина зависит от того,
  как давно играет сам игрок.
- `rating_current` в таблице турнира и в поиске игроков — сегодняшнее
  значение, а не рейтинг на день события. Отдельных матчей страница
  турнира не содержит вовсе — их восстанавливает `get_tournament_matches`
  из профилей участников. Восстановленный рейтинг на дату турнира даёт
  `get_tournament(..., ratings_at_event=True)`: добавляет каждой строке
  `rating_at_event`, которое может быть `null`.
- Исход матча (`result`) принимает шесть значений: `win`, `loss`,
  `walkover_win`, `walkover_loss`, `not_played`, `unparsed`.
  `not_played` (счёт 0:0, матч не сыгран) не считается ни победой, ни
  поражением.
- Пометка руки вынесена из имени в поле `hand`. Сайт заводит под
  нерабочую руку отдельный профиль с отдельным идентификатором, поэтому
  один человек может иметь несколько профилей с разными id — сервер их не
  связывает.
- `summary["rating_min"]`, `["rating_max"]` и `["rating_avg"]` в профиле —
  не значения за карьеру, а величины в пределах окна примерно в тридцать
  периодов: у игрока с 87 периодами сводка показывает минимум 150.04 при
  настоящем минимуме 70.31 по `periods`.
- `summary["rated_periods"]` упирается в 30 и у активных игроков занижает
  число периодов. Считать надо по длине `periods`.
- Поиск игроков режется на 500 строках, турниров — на 20 в одном запросе,
  пагинации нет.
- Блок `summary` в профиле отстаёт на один недельный период от списка
  матчей. Для подсчётов используйте `matches`.
- Сайт отвечает за 4–17 секунд. Запросы идут последовательно намеренно.

## Разработка

Тесты парсеров работают на сохранённых страницах сайта. В репозитории их
нет: они содержат ФИО реальных игроков, а r.ttw.ru просит не обходить себя
автоматически. Поэтому перед первым прогоном их надо получить — это
четырнадцать запросов с паузами, около полутора минут:

```bash
git clone https://github.com/lappi/ttw-mcp && cd ttw-mcp
uv run python scripts/fetch_fixtures.py   # получить/обновить фикстуры
uv run pytest                             # 220 тестов, без сети
uv run pytest -m smoke                    # 4 теста против живого сайта
```

Без фикстур `pytest` сообщит, каким скриптом их восстановить.

Дизайн: `docs/superpowers/specs/2026-09-22-ttw-mcp-design.md` и
`docs/superpowers/specs/2026-09-22-improvements-design.md`.

## Лицензия

MIT — см. [LICENSE](LICENSE).

TDQS

A4.3/5.0

Scored across 5 tools

Disambiguation5/5

Each tool has a clearly distinct role: search vs. detail for both tournaments and players, plus a dedicated head-to-head endpoint. There is no overlap or ambiguity between the two search tools and their corresponding getter tools.

Naming Consistency5/5

All tool names follow the same snake_case verb_noun pattern: search_tournaments, get_tournament, search_players, get_player, get_head_to_head. The naming is uniform and predictable.

Tool Count5/5

Five tools is well-scoped for a read-only tournament and player statistics server: two search endpoints, two detail endpoints, and one head-to-head endpoint. Each tool covers a distinct need without bloat.

Completeness5/5

The tool surface covers the core read-only domain: finding tournaments, inspecting tournament results, searching players, viewing detailed player histories, and comparing two players. Documented source limitations such as missing tournament match lists are explicitly handled via player profiles, so no critical workflow dead-ends.

Maintenance

ActivityMaintained
ResponsivenessNo issues