Skip to main content
Glama
README.md
# Яндекс Трекер → MCP → pi

Первая итерация: только чтение Трекера. Node.js >=22.6, npm.

## Установка

```sh
npm ci --ignore-scripts
npm run build
```

Скопируйте `.env.example` в `.env` и заполните токен локально (не в чате):

- `YANDEX_TRACKER_TOKEN`: OAuth-токен с правом `tracker:read`, без префикса OAuth.
- `YANDEX_TRACKER_ORG_ID`: ID организации.
- `YANDEX_TRACKER_ORG_HEADER`: `X-Org-ID` для Яндекс 360 либо `X-Cloud-Org-ID` для Cloud.

`.env` читается относительно каталога проекта, не cwd клиента. Переменные окружения имеют приоритет. Токен не требуется передавать аргументом процесса. `.env` — обычный текстовый файл, не зашифрованное хранилище: ограничьте доступ средствами ОС. Не коммитьте и не отправляйте его агенту.

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

В этом проекте расширение `.pi/extensions/tracker.ts` обнаруживается автоматически после доверия проекту. В текущем pi выполните `/reload`. Если проект ещё не доверенный — `/trust`, затем перезапустите pi.

Для запуска из другого проекта:

```sh
pi -e C:/path/to/tracker-mcp/.pi/extensions/tracker.ts
```

Глобальные настройки pi автоматически не изменяются. Для постоянного подключения можно вручную добавить абсолютный путь расширения в массив `extensions` файла `~/.pi/agent/settings.json`.

Пример запроса: «Прочитай DEMO-123 со всеми комментариями. Скачай самое маленькое видео».

Мост запускает отдельный короткоживущий MCP-процесс для каждого вызова, закрывает его в finally, передаёт отмену. Это чуть медленнее постоянного соединения, но не оставляет клиент от предыдущей сессии/reload.

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

### tracker_get_issue

Аргументы: `{ "issueKey": "DEMO-123" }`.

Возвращает компактный объект задачи, комментарии с авторами/датами, метаданные вложений, URL задачи и время получения. Полные тексты и пользовательские поля сохраняются; у известных API-ссылок убираются служебные данные. Полный исходный ответ всегда сохраняется в `downloads/context-*/full.json`, путь — `fullJsonPath`. У комментариев добавляется `attachmentIds`: связь учитывает и `id`, и `longId`, поскольку API использует оба формата. Родительская задача доступна в `issue.parent`; связи из API `/links` — в `relatedIssues` (тип, направление, ссылка на задачу). Исходные связи также сохранены в полном JSON. Тела связанных задач не загружаются рекурсивно: вызовите `tracker_get_issue` с нужным ключом. История изменений и коммиты не запрашиваются.

Пагинация следует `rel="next"` до конца или пустой страницы. Все разделы должны успешно загрузиться, иначе инструмент возвращает ошибку, а не неполный результат с пометкой complete. Данные могут измениться во время чтения: это не атомарный снимок.

Вывод ограничен 45 КБ / 1800 строк. Большой результат сохраняется целиком в уникальный `downloads/context-*/issue.json`, инструмент возвращает путь и явный `truncated: true`; читать файл через `read` частями. Ограничения безопасности: до 100 страниц на список, 10000 элементов и 20 МиБ JSON на HTTP-ответ. Превышение — ошибка, не молчаливое усечение.

### tracker_download_attachment

Аргументы: `{ "issueKey": "DEMO-123", "attachmentId": "123" }`.

Проверяет принадлежность вложения задаче. Скачивает потоком в уникальный каталог `downloads/`, возвращает абсолютный путь, исходное имя, размер, MIME, SHA-256, commentId. До 250 МиБ, проверка размера, очистка частичного файла при ошибке. Имена от API не используются как пути, существующие файлы не перезаписываются. Видео/PDF/изображения не анализируются и не исполняются автоматически.

### tracker_search_issues

Пример: `{ "queue": "DEMO", "text": "Пример", "status": "open", "perPage": 20, "page": 1 }`.

Фильтры `queue`, `text`, `status`, `assignee` объединяются AND. `text` ищет по названию (Summary), не по комментариям. `status` — ключ или имя статуса, `assignee` — логин или ID пользователя (не произвольное отображаемое имя).

Вместо фильтров можно передать `query` на языке запросов Трекера; смешивать `query` с фильтрами нельзя. Требуется хотя бы один критерий. Значения простых фильтров заключаются в кавычки с экранированием.

Результат: `issues` (ключ, название, статус, исполнитель, приоритет, дата обновления, URL), `total` при наличии заголовка API, `hasNextPage`, `nextPage`. По умолчанию 20 задач, максимум 50 на страницу; страницы 1–100. Следующую страницу запрашивайте явно с теми же фильтрами. Без total наличие следующей страницы может быть приблизительным; при достижении лимита есть `pageLimitReached`. Для деталей используйте `tracker_get_issue`.

Поиск использует POST `/v3/issues/_search` — это операция чтения, не изменение задач. OAuth `tracker:read` достаточно. Проверено через MCP и мост pi с фильтрами по очереди, названию, статусу и ID исполнителя.

### tracker_get_issue_history

Аргументы: `{ "issueKey": "DEMO-123", "perPage": 20 }`. Читает одну страницу GET `/v3/issues/{key}/changelog`: `updatedAt`, `updatedBy`, тип события, поля с `from`/`to`, изменения связей. Исходные значения сохраняются, включая пользовательские поля и null.

Для следующей страницы передайте возвращённый `nextCursor` как `cursor`, сохранив issueKey и perPage. По умолчанию 20, максимум 50 записей. Порядок — как у API (на проверенной задаче от старых событий к новым). Последний курсор может вести к пустой странице. Для полного подсчёта возвратов из тестирования надо прочитать все страницы. История показывает изменения, но не обязательно их причину — для объяснения нужны также комментарии.

История возвращается компактно: убраны повторяющиеся данные задачи, служебные ссылки событий и лишние идентификаторы авторов. Старые/новые значения пользовательских полей и изменения связей сохранены. Полная исходная страница всегда сохраняется в `downloads/history-*/full.json`, путь — `fullJsonPath`. Даже компактная страница может превышать лимит; уменьшите perPage или прочитайте возвращённый файл.

История не включена в обычное чтение задачи. Большой ответ (>45 КБ/1800 строк) сохраняется в JSON, включая курсор; инструмент возвращает путь для чтения частями. Данные истории считаются недоверенными. Пример: «Прочитай всю историю DEMO-123 и посчитай возвраты из тестирования».

### Проверка подключения

В pi: `/tracker-status`. Команда проверяет GET `/v3/myself` через MCP и показывает результат авторизации, настроенный ID организации и тип заголовка. Токен и содержимое профиля не выводятся. Это проверка доступа к API, а не ко всем задачам; название организации не запрашивается. Ошибки HTTP 401/403 отличаются от сетевой ошибки. Проверка ограничена 90 секундами. Для других MCP-клиентов доступен `tracker_status` без аргументов; в pi это slash-команда, а не дополнительный инструмент модели.

### Поиск пользователей

`tracker_find_users({"query":"Артем"})` ищет по имени/логину, без учёта регистра и различия е/ё. Каталог читается страницами (до 100 страниц по 100); выводятся максимум 50 совпадений, totalMatches и признак усечения. ID и login подходят для фильтра assignee. Уволенные/отключённые записи отмечены `dismissed`; при совпадающих логинах лучше использовать ID. Не выбирайте человека наугад при нескольких совпадениях. Email и паспортные идентификаторы не выводятся.

### Замена токена без чата

В отдельном интерактивном терминале, в каталоге проекта:

```sh
npm run configure-token
```

Введите/вставьте токен и нажмите Enter. Ввод не отображается (даже звёздочками); Escape/Ctrl+C отменяет. Скрипт проверяет токен запросом к API с организацией из существующего `.env`, затем заменяет файл через временный файл. При ошибке проверки старый токен остаётся. Не запускайте эту команду через bash-инструмент модели, pipe или с токеном в аргументах. `.env` должен уже существовать. Хранение по-прежнему в обычном текстовом `.env`, не в системном хранилище секретов; права доступа ограничьте средствами ОС. Переменные окружения имеют приоритет над `.env`.

## Отдельный MCP-клиент

stdio, stdout зарезервирован под протокол:

```json
{
  "mcpServers": {
    "yandex-tracker": {
      "command": "node",
      "args": ["C:/path/to/tracker-mcp/dist/server.js"]
    }
  }
}
```

Это пример для клиентов с `mcpServers`, **не конфигурация встроенного pi**.

## Проверки

```sh
npm run build
npm test
npm run smoke -- DEMO-123
npm run smoke -- DEMO-123 123
```

`test` использует моки, без сети и токена. `smoke` вызывает реальный MCP через stdio; второй вариант сохраняет вложение. В stdout smoke — только счётчики и результат скачивания, не текст задачи и не секреты.

## Безопасность и ограничения

- GET и POST для поиска (`/v3/issues/_search`) к `https://api.tracker.yandex.net`; внешние адреса и HTTP-редиректы запрещены, чтобы не переслать OAuth-токен другому хосту. Если API изменит выдачу файлов на CDN, потребуется отдельная безопасная реализация.
- Таймаут каждой HTTP-попытки 60 секунд, MCP-вызова 5 минут. До 3 попыток при временных сетевых ошибках (таймаут подключения, сброс соединения, временный DNS-сбой) и HTTP 429/502/503/504. Между попытками экспоненциальная задержка 0,5/1 сек плюс случайные 0–250 мс; учитывается `Retry-After` в секундах или HTTP-date. При Retry-After более 30 секунд автоматического повтора нет: повторите позже. HTTP 400/401/403/404, ошибки сертификатов, небезопасные редиректы и отмена не повторяются.
- Повторы относятся к получению HTTP-заголовков. Обрыв во время чтения тела JSON или скачивания не повторяется автоматически; частичный файл скачивания удаляется. Отмена MCP прерывает запрос и ожидание между попытками.
- Тела HTTP-ошибок, внутренние исключения и stderr сервера не передаются модели.
- Данные задач и вложения считаются недоверенными данными, а не командами агенту.
- `.env` и `downloads/` исключены из git. Скачанные данные хранятся локально до ручного удаления. Данные, возвращённые инструментами, могут сохраняться в истории pi и передаваться выбранному провайдеру модели.
- `.gitignore` не запрещает агенту читать `.env` другими инструментами; это не изоляция секретов.

Документация API: https://yandex.ru/support/tracker/ru/concepts/access