@vitalyostanin/youtrack-mcp 0.10.2 → 0.12.0

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 (128) hide show
  1. package/README-ru.md +28 -13
  2. package/README.md +61 -19
  3. package/dist/package.json +17 -8
  4. package/dist/src/__tests__/config.test.d.ts +2 -0
  5. package/dist/src/__tests__/config.test.js +47 -0
  6. package/dist/src/__tests__/setup.d.ts +2 -0
  7. package/dist/src/__tests__/setup.js +12 -0
  8. package/dist/src/__tests__/smoke.test.d.ts +2 -0
  9. package/dist/src/__tests__/smoke.test.js +7 -0
  10. package/dist/src/__tests__/youtrack-client.delete-link.test.d.ts +2 -0
  11. package/dist/src/__tests__/youtrack-client.delete-link.test.js +93 -0
  12. package/dist/src/__tests__/youtrack-client.normalize-error.test.d.ts +2 -0
  13. package/dist/src/__tests__/youtrack-client.normalize-error.test.js +71 -0
  14. package/dist/src/__tests__/youtrack-client.process-batch.test.d.ts +2 -0
  15. package/dist/src/__tests__/youtrack-client.process-batch.test.js +63 -0
  16. package/dist/src/__tests__/youtrack-client.read-pagination.test.d.ts +2 -0
  17. package/dist/src/__tests__/youtrack-client.read-pagination.test.js +75 -0
  18. package/dist/src/__tests__/youtrack-client.resolve-id.test.d.ts +2 -0
  19. package/dist/src/__tests__/youtrack-client.resolve-id.test.js +77 -0
  20. package/dist/src/__tests__/youtrack-client.single-flight.test.d.ts +2 -0
  21. package/dist/src/__tests__/youtrack-client.single-flight.test.js +82 -0
  22. package/dist/src/__tests__/youtrack-client.timeouts.test.d.ts +2 -0
  23. package/dist/src/__tests__/youtrack-client.timeouts.test.js +27 -0
  24. package/dist/src/__tests__/youtrack-client.url-encoding.test.d.ts +2 -0
  25. package/dist/src/__tests__/youtrack-client.url-encoding.test.js +60 -0
  26. package/dist/src/config.d.ts +2 -8
  27. package/dist/src/config.js +25 -6
  28. package/dist/src/constants.d.ts +20 -0
  29. package/dist/src/constants.js +21 -0
  30. package/dist/src/server.js +28 -3
  31. package/dist/src/tools/__tests__/article-search-tools.test.d.ts +2 -0
  32. package/dist/src/tools/__tests__/article-search-tools.test.js +31 -0
  33. package/dist/src/tools/__tests__/attachment-tools.test.d.ts +2 -0
  34. package/dist/src/tools/__tests__/attachment-tools.test.js +94 -0
  35. package/dist/src/tools/__tests__/destructive-confirmation.test.d.ts +2 -0
  36. package/dist/src/tools/__tests__/destructive-confirmation.test.js +38 -0
  37. package/dist/src/tools/__tests__/issue-activity-tools.test.d.ts +2 -0
  38. package/dist/src/tools/__tests__/issue-activity-tools.test.js +69 -0
  39. package/dist/src/tools/__tests__/issue-search-tools.test.d.ts +2 -0
  40. package/dist/src/tools/__tests__/issue-search-tools.test.js +25 -0
  41. package/dist/src/tools/__tests__/issue-status-tools.test.d.ts +2 -0
  42. package/dist/src/tools/__tests__/issue-status-tools.test.js +98 -0
  43. package/dist/src/tools/__tests__/workitem-tools.test.d.ts +2 -0
  44. package/dist/src/tools/__tests__/workitem-tools.test.js +72 -0
  45. package/dist/src/tools/article-search-tools.d.ts +44 -21
  46. package/dist/src/tools/article-search-tools.js +40 -34
  47. package/dist/src/tools/article-tools.js +79 -75
  48. package/dist/src/tools/attachment-tools.d.ts +101 -0
  49. package/dist/src/tools/attachment-tools.js +134 -127
  50. package/dist/src/tools/issue-activity-tools.d.ts +149 -0
  51. package/dist/src/tools/issue-activity-tools.js +83 -65
  52. package/dist/src/tools/issue-link-tools.js +70 -52
  53. package/dist/src/tools/issue-list-tools.js +58 -66
  54. package/dist/src/tools/issue-search-tools.d.ts +67 -43
  55. package/dist/src/tools/issue-search-tools.js +53 -104
  56. package/dist/src/tools/issue-star-tools.d.ts +11 -0
  57. package/dist/src/tools/issue-star-tools.js +76 -57
  58. package/dist/src/tools/issue-status-tools.d.ts +194 -0
  59. package/dist/src/tools/issue-status-tools.js +77 -71
  60. package/dist/src/tools/issue-tools.js +281 -256
  61. package/dist/src/tools/project-tools.js +42 -27
  62. package/dist/src/tools/service-info.js +22 -18
  63. package/dist/src/tools/user-tools.js +69 -50
  64. package/dist/src/tools/users-activity-tools.d.ts +45 -22
  65. package/dist/src/tools/users-activity-tools.js +19 -20
  66. package/dist/src/tools/workitem-report-tools.js +83 -68
  67. package/dist/src/tools/workitem-tools.d.ts +376 -0
  68. package/dist/src/tools/workitem-tools.js +252 -213
  69. package/dist/src/types.d.ts +43 -0
  70. package/dist/src/types.js +14 -1
  71. package/dist/src/utils/__tests__/file-download.test.d.ts +2 -0
  72. package/dist/src/utils/__tests__/file-download.test.js +77 -0
  73. package/dist/src/utils/__tests__/file-storage.test.d.ts +2 -0
  74. package/dist/src/utils/__tests__/file-storage.test.js +61 -0
  75. package/dist/src/utils/__tests__/path-safety.test.d.ts +2 -0
  76. package/dist/src/utils/__tests__/path-safety.test.js +49 -0
  77. package/dist/src/utils/__tests__/streaming-client.test.d.ts +2 -0
  78. package/dist/src/utils/__tests__/streaming-client.test.js +90 -0
  79. package/dist/src/utils/__tests__/validators.test.d.ts +2 -0
  80. package/dist/src/utils/__tests__/validators.test.js +65 -0
  81. package/dist/src/utils/date.js +10 -4
  82. package/dist/src/utils/file-download.d.ts +19 -3
  83. package/dist/src/utils/file-download.js +106 -30
  84. package/dist/src/utils/file-storage.d.ts +27 -15
  85. package/dist/src/utils/file-storage.js +142 -123
  86. package/dist/src/utils/issue-query.js +17 -10
  87. package/dist/src/utils/mappers.js +7 -7
  88. package/dist/src/utils/path-safety.d.ts +18 -0
  89. package/dist/src/utils/path-safety.js +47 -0
  90. package/dist/src/utils/streaming-client.d.ts +13 -7
  91. package/dist/src/utils/streaming-client.js +87 -82
  92. package/dist/src/utils/tool-args.d.ts +31 -0
  93. package/dist/src/utils/tool-args.js +46 -0
  94. package/dist/src/utils/tool-handler.d.ts +18 -0
  95. package/dist/src/utils/tool-handler.js +35 -0
  96. package/dist/src/utils/validators.d.ts +30 -0
  97. package/dist/src/utils/validators.js +75 -0
  98. package/dist/src/youtrack-client/activities.d.ts +24 -0
  99. package/dist/src/youtrack-client/activities.js +70 -0
  100. package/dist/src/youtrack-client/articles.d.ts +16 -0
  101. package/dist/src/youtrack-client/articles.js +111 -0
  102. package/dist/src/youtrack-client/attachments.d.ts +11 -0
  103. package/dist/src/youtrack-client/attachments.js +135 -0
  104. package/dist/src/youtrack-client/base.d.ts +120 -0
  105. package/dist/src/youtrack-client/base.js +335 -0
  106. package/dist/src/youtrack-client/batch.d.ts +11 -0
  107. package/dist/src/youtrack-client/batch.js +157 -0
  108. package/dist/src/youtrack-client/comments.d.ts +15 -0
  109. package/dist/src/youtrack-client/comments.js +76 -0
  110. package/dist/src/youtrack-client/core.d.ts +15 -0
  111. package/dist/src/youtrack-client/core.js +173 -0
  112. package/dist/src/youtrack-client/index.d.ts +6 -0
  113. package/dist/src/youtrack-client/index.js +17 -0
  114. package/dist/src/youtrack-client/issue-search.d.ts +12 -0
  115. package/dist/src/youtrack-client/issue-search.js +417 -0
  116. package/dist/src/youtrack-client/links.d.ts +15 -0
  117. package/dist/src/youtrack-client/links.js +286 -0
  118. package/dist/src/youtrack-client/stars.d.ts +16 -0
  119. package/dist/src/youtrack-client/stars.js +160 -0
  120. package/dist/src/youtrack-client/state.d.ts +13 -0
  121. package/dist/src/youtrack-client/state.js +162 -0
  122. package/dist/src/youtrack-client/users-projects.d.ts +30 -0
  123. package/dist/src/youtrack-client/users-projects.js +162 -0
  124. package/dist/src/youtrack-client/workitems.d.ts +46 -0
  125. package/dist/src/youtrack-client/workitems.js +351 -0
  126. package/dist/src/youtrack-client.d.ts +1 -177
  127. package/dist/src/youtrack-client.js +1 -2347
  128. package/package.json +17 -8
package/README-ru.md CHANGED
@@ -40,6 +40,8 @@ MCP сервер для полноценной интеграции с YouTrack
40
40
  - [Конфигурация для VS Code Cline](#конфигурация-для-vs-code-cline)
41
41
  - [Инструменты MCP](#инструменты-mcp)
42
42
  - [Параметры сохранения файлов](#параметры-сохранения-файлов)
43
+ - [Пагинация](#пагинация)
44
+ - [Подтверждение деструктивных операций](#подтверждение-деструктивных-операций)
43
45
  - [Сервис](#сервис)
44
46
  - [Задачи](#задачи)
45
47
  - [Связи задач](#связи-задач)
@@ -62,6 +64,7 @@ MCP сервер для полноценной интеграции с YouTrack
62
64
  - `YOUTRACK_PRE_HOLIDAYS` — опциональный список предпраздничных дат через запятую, для которых норма времени уменьшается автоматически
63
65
  - `YOUTRACK_USER_ALIASES` — опциональный список соответствий `alias:login` через запятую (например, `me:vyt,petya:p.petrov`), используется при автоматическом выборе исполнителей
64
66
  - `YOUTRACK_DEFAULT_PROJECT` — опциональный код проекта (shortName или ID), используемый по умолчанию при создании задач/статей и для преобразования коротких идентификаторов (например, `1234` → `PROJ-1234`)
67
+ - `YOUTRACK_OUTPUT_DIR` — опциональный корневой каталог для файлов, создаваемых через `saveToFile` и `downloadToFile`. По умолчанию используется текущий рабочий каталог. Абсолютные пути и сегменты с `..` (выход за пределы корня) отвергаются ради безопасности
65
68
 
66
69
 
67
70
  ## Установка
@@ -278,11 +281,23 @@ YOUTRACK_TOKEN = "perm:your-token-here"
278
281
  Многие инструменты поддерживают опциональные параметры сохранения файлов для работы с большими датасетами:
279
282
 
280
283
  - `saveToFile` — boolean, сохраняет результаты в JSON файл вместо возврата напрямую (полезно для больших датасетов)
281
- - `filePath` — string, пользовательский путь к файлу (опционально, генерируется автоматически если не указан, директория создается при необходимости)
284
+ - `filePath` — string, пользовательский путь к файлу относительно `YOUTRACK_OUTPUT_DIR` (опционально, генерируется автоматически если не указан, директория создается при необходимости)
282
285
  - `format` — string, формат вывода при сохранении в файл: `jsonl` (JSON Lines) или `json` (JSON массив). По умолчанию `jsonl`
283
286
  - `overwrite` — boolean, разрешить перезапись существующих файлов при использовании явного пути к файлу. По умолчанию `false`
284
287
 
285
- Когда `saveToFile` установлено в `true`, инструменты возвращают метаданные о сохраненном файле вместо полных данных.
288
+ Когда `saveToFile` установлено в `true`, инструменты возвращают метаданные о сохранённом файле вместо полных данных. Все пути разрешаются относительно `YOUTRACK_OUTPUT_DIR`; абсолютные пути и сегменты `..` отвергаются.
289
+
290
+ ### Пагинация
291
+
292
+ Большинство read-инструментов поддерживают `limit` (по умолчанию 100, максимум 200) и `skip` (по умолчанию 0); оба параметра прокидываются в YouTrack как `$top`/`$skip`. В ответах с агрегированным счётчиком используется поле `returned` (количество элементов на текущей странице) вместо `total`. Используйте последовательные значения `skip` для постраничного обхода больших выборок.
293
+
294
+ ### Подтверждение деструктивных операций
295
+
296
+ Инструменты, удаляющие данные, требуют явного литерала `confirmation: true` во входных параметрах:
297
+
298
+ - `issue_attachment_delete`
299
+ - `issue_link_delete`
300
+ - `workitem_delete`
286
301
 
287
302
  ### Сервис
288
303
 
@@ -298,7 +313,7 @@ YOUTRACK_TOKEN = "perm:your-token-here"
298
313
  | `issues_lookup` | Получить информацию о нескольких задачах YouTrack (пакетный режим, макс 50). **Использование:** Эффективное получение информации о множестве задач одновременно. **Возвращает:** те же поля, что и `issue_lookup`. **Ограничение:** максимум 50 задач за запрос | `issueIds[]` — массив кодов задач (например, `['PROJ-123', 'PROJ-124']`), макс 50; `briefOutput` — опционально (по умолчанию `true`) |
299
314
  | `issue_details` | Режимы краткий/полный для деталей задачи. **Использование:** Просмотр деталей задачи. **Краткий (по умолчанию):** предопределённые поля — id, idReadable, summary, description, wikifiedDescription, usesMarkdown, created, updated, resolved, project, parent, assignee, reporter, updater, watchers(hasStar). **Полный (`briefOutput=false`):** добавляет `customFields(id,name,value(id,name,presentation),$type,possibleEvents(id,presentation))`, включая поле `State`. | `issueId` — код задачи; `briefOutput` — опционально (по умолчанию `true`); `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
300
315
  | `issues_details` | Режимы краткий/полный для множества задач (макс 50). **Краткий:** предопределённые поля. **Полный (`briefOutput=false`):** добавляет `customFields` для каждой задачи. **Замечание:** объём ответа может быть большим, поэтому по умолчанию используется краткий режим. **Ограничение:** максимум 50 задач за запрос | `issueIds[]` — массив кодов задач, макс 50; `briefOutput` — опционально (по умолчанию `true`); `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
301
- | `issue_comments` | Комментарии задачи. **Использование:** Просмотр всех комментариев к задаче. **Возвращает:** предопределенные поля - id, text, textPreview, usesMarkdown, author (id, login, name), created, updated, commentUrl (прямая ссылка на комментарий) | `issueId` — код задачи; `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
316
+ | `issue_comments` | Комментарии задачи с серверной пагинацией. **Использование:** Просмотр всех комментариев к задаче. **Возвращает:** предопределенные поля - id, text, textPreview, usesMarkdown, author (id, login, name), created, updated, commentUrl (прямая ссылка на комментарий) | `issueId` — код задачи; `limit` (по умолчанию 100, макс 200), `skip` для пагинации; `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
302
317
  | `issues_comments` | Комментарии для нескольких задач (пакетный режим, макс 50). **Использование:** Эффективное получение комментариев для множества задач одновременно. **Ограничение:** максимум 50 задач за запрос | `issueIds[]` — массив кодов задач, макс 50; `briefOutput` — опционально (по умолчанию `true`); `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
303
318
  | `issue_create` | Создание новой задачи в YouTrack. **Использование:** Создание задач, подзадач (через `parentIssueId`), назначение исполнителя при создании, одновременное создание дополнительных связей. **Возвращает:** стандартные поля созданной задачи (id, idReadable, summary, description, wikifiedDescription, usesMarkdown, project, parent, assignee). Пользовательские поля не включены. | `projectId` — внутренний ID проекта, `summary`, опционально `description`, `parentIssueId`, `assigneeLogin`, `stateName`, `usesMarkdown`, `links[]` — массив объектов связи |
304
319
  | `issue_update` | Обновление существующей задачи. **Использование:** Изменение summary, description, родительской задачи. **Примечание:** для изменения исполнителя используйте `issue_assign`. Пустая строка в `parentIssueId` удаляет родителя. **Возвращает:** стандартные поля обновленной задачи | `issueId` — ID или код задачи, опционально `summary`, `description`, `parentIssueId` (пустая строка очищает родителя), `usesMarkdown` |
@@ -312,10 +327,10 @@ YOUTRACK_TOKEN = "perm:your-token-here"
312
327
 
313
328
  | Tool | Описание | Основные параметры |
314
329
  | --- | --- | --- |
315
- | `issue_links` | Список связей (линков) для задачи: «relates to», «duplicate», «parent/child». Возвращает id связи, направление (inward/outbound), тип связи и краткую информацию о связанной задаче | `issueId` — код задачи |
330
+ | `issue_links` | Список связей (линков) для задачи с серверной пагинацией. Возвращает id связи, направление (inward/outbound), тип связи и краткую информацию о связанной задаче | `issueId` — код задачи; `limit` (по умолчанию 100, макс 200), `skip` для пагинации |
316
331
  | `issue_link_types` | Список доступных типов связей в YouTrack. Полезно для подбора корректного типа при создании связи | — |
317
332
  | `issue_link_add` | Создание связи между двумя задачами | `sourceId` — исходная задача, `targetId` — целевая задача, `linkType` — имя или id типа связи (например, `Relates` или UUID), опционально `direction` (`outbound` или `inbound`) |
318
- | `issue_link_delete` | Удаление существующей связи по её id для указанной задачи | `issueId` код задачи, `linkId` — id связи для удаления |
333
+ | `issue_link_delete` | Удаление существующей связи по её id (требует `confirmation: true`) | `issueId`, `linkId`, опционально `targetId`, `confirmation` — обязательно булев литерал `true` |
319
334
 
320
335
  **issue_create — дополнительные параметры:**
321
336
 
@@ -348,14 +363,14 @@ YOUTRACK_TOKEN = "perm:your-token-here"
348
363
 
349
364
  | Tool | Описание | Основные параметры |
350
365
  | --- | --- | --- |
351
- | `workitems_list` | Получение трудозатрат текущего или указанного пользователя. **Использование:** Просмотр учтенного времени, фильтрация по задаче, автору, периоду. **Возвращает:** предопределенные поля - id, date, duration (minutes, presentation), text, textPreview, usesMarkdown, description, issue (id, idReadable), author (id, login, name, email) | Опционально `issueId`, `author` (логин пользователя), `startDate` (формат YYYY-MM-DD, timestamp или date-time), `endDate`, `allUsers`; `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
352
- | `workitems_all_users` | Получение трудозатрат всех пользователей. **Использование:** Просмотр времени всей команды, анализ загрузки проекта. **Возвращает:** те же поля, что и `workitems_list` | Опционально `issueId`, `startDate`, `endDate`; `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
353
- | `workitems_for_users` | Получение трудозатрат выбранных пользователей. **Использование:** Просмотр времени конкретных участников команды, сравнение загрузки между пользователями. **Возвращает:** те же поля, что и `workitems_list` | `users[]` — массив логинов пользователей, опционально `issueId`, `startDate`, `endDate`; `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
366
+ | `workitems_list` | Получение трудозатрат текущего или указанного пользователя с серверной пагинацией. **Использование:** Просмотр учтенного времени, фильтрация по задаче, автору, периоду | Опционально `issueId`, `author`, `startDate`, `endDate`, `allUsers`; `limit` (по умолчанию 100, макс 200), `skip` для пагинации; `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
367
+ | `workitems_all_users` | Получение трудозатрат всех пользователей с серверной пагинацией. **Возвращает:** те же поля, что и `workitems_list` | Опционально `issueId`, `startDate`, `endDate`; `limit` (по умолчанию 100, макс 200), `skip` для пагинации; `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
368
+ | `workitems_for_users` | Получение трудозатрат выбранных пользователей с per-user пагинацией. **Возвращает:** те же поля, что и `workitems_list` | `users[]` — массив логинов пользователей, опционально `issueId`, `startDate`, `endDate`; `limit` (по умолчанию 100, макс 200), `skip` применяются на каждого пользователя; `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
354
369
  | `workitems_recent` | Получение последных записей трудозатрат с сортировкой по времени обновления (новые первыми). **Использование:** Быстрый просмотр недавних записей времени, мониторинг текущей активности. **Возвращает:** те же поля, что и `workitems_list`. **Ограничение:** по умолчанию 50 записей, макс 200 | Опционально `users[]` (по умолчанию текущий пользователь), `limit` (по умолчанию 50, макс 200); `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
355
370
  | `workitem_create` | Создание записи трудозатрат. **Использование:** Учет времени на задачу за конкретную дату. **Возвращает:** предопределенные поля созданной записи. **Примечание:** может создавать дубликаты, для предотвращения используйте `workitem_create_idempotent` | `issueId` — ID задачи, `date` (формат YYYY-MM-DD, timestamp или date-time), `minutes` — количество минут (>0), опционально `summary` — краткий текст, `description`, `usesMarkdown` |
356
371
  | `workitem_create_idempotent` | Создание записи без дублей (по описанию и дате). **Использование:** Безопасное создание записи времени с предотвращением дублирования. Проверяет существование похожей записи по описанию. **Возвращает:** предопределенные поля созданной или найденной записи | `issueId` — ID задачи, `date`, `minutes`, `description` — текст для поиска существующей записи, опционально `usesMarkdown` |
357
372
  | `workitem_update` | Обновление записи трудозатрат. **Использование:** Исправление учтенного времени, даты или описания. **Возвращает:** предопределенные поля обновленной записи | `issueId` — ID задачи, `workItemId` — ID записи трудозатрат, опционально `date`, `minutes`, `summary` — новый текст, `description`, `usesMarkdown` |
358
- | `workitem_delete` | Удаление записи трудозатрат. **Использование:** Удаление ошибочно созданных или неактуальных записей | `issueId` — ID задачи, `workItemId` — ID записи трудозатрат |
373
+ | `workitem_delete` | Удаление записи трудозатрат (требует `confirmation: true`). **Использование:** Удаление ошибочно созданных или неактуальных записей | `issueId`, `workItemId`, `confirmation`обязательно булев литерал `true` |
359
374
  | `workitems_create_period` | Массовое создание трудозатрат по диапазону дат. **Использование:** Учет регулярной работы за период, автоматическое исключение выходных и праздников. **Возвращает:** массив созданных записей с предопределенными полями. **Примечание:** автоматически пропускает выходные при `excludeWeekends=true` и даты из списка `holidays` | `issueId`, `startDate`, `endDate`, `minutes` — минут в день, опционально `summary`, `description`, `usesMarkdown`, `excludeWeekends` — исключить субботы и воскресенья, `excludeHolidays`, `holidays[]` — массив дат праздников (YYYY-MM-DD, timestamp или date-time), `preHolidays[]` — массив предпраздничных дат |
360
375
  | `workitems_report_summary` | Сводный отчёт по трудозатратам. **Использование:** Анализ загрузки за период, сравнение плана и факта, выявление недостатка/избытка часов. **Возвращает:** общую статистику - общее количество минут, ожидаемое количество, отклонение, количество рабочих дней, данные по дням | Опционально `author` (логин), `issueId`, `startDate`, `endDate`, `expectedDailyMinutes` — норма минут в день (например, 480 для 8 часов), `excludeWeekends`, `excludeHolidays`, `holidays[]`, `preHolidays[]`, `allUsers` — включить всех пользователей; `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
361
376
  | `workitems_report_invalid` | Список дней с отклонением от нормы времени. **Использование:** Поиск дней с недостатком или избытком учтенного времени, контроль соблюдения нормы. **Возвращает:** массив дней, где факт отличается от нормы, с детализацией по записям | Те же параметры, что и для `workitems_report_summary`; `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
@@ -366,10 +381,10 @@ YOUTRACK_TOKEN = "perm:your-token-here"
366
381
 
367
382
  | Tool | Описание | Основные параметры |
368
383
  | --- | --- | --- |
369
- | `users_list` | Список всех пользователей YouTrack. **Использование:** Просмотр доступных пользователей, поиск логинов для назначения задач. **Возвращает:** предопределенные поля - id, login, name, fullName, email | `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
384
+ | `users_list` | Список пользователей YouTrack с серверной пагинацией. **Использование:** Просмотр доступных пользователей, поиск логинов для назначения задач. **Возвращает:** предопределенные поля - id, login, name, fullName, email | `limit` (по умолчанию 100, макс 200), `skip` для пагинации; `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
370
385
  | `user_get` | Получение пользователя по логину. **Использование:** Проверка существования пользователя, получение деталей профиля. **Возвращает:** предопределенные поля - id, login, name, fullName, email | `login` — логин пользователя |
371
386
  | `user_current` | Получение текущего аутентифицированного пользователя. **Использование:** Проверка, под каким пользователем работает API токен, получение собственного ID для операций. **Возвращает:** предопределенные поля - id, login, name, fullName, email | — |
372
- | `projects_list` | Список всех проектов YouTrack. **Использование:** Просмотр доступных проектов, поиск ID проекта для создания задач. **Возвращает:** предопределенные поля - id, shortName, name | |
387
+ | `projects_list` | Список проектов YouTrack (по умолчанию авто-пагинация; для явной пагинации передайте `limit`/`skip`). **Возвращает:** предопределенные поля - id, shortName, name | Опционально `limit` (макс 200), `skip` |
373
388
  | `project_get` | Получение проекта по короткому имени. **Использование:** Получение внутреннего ID проекта по короткому имени (например, `PROJ` → внутренний ID) для создания задач. **Возвращает:** предопределенные поля - id, shortName, name | `shortName` — короткое имя проекта (например, `PROJ`, `BC`) |
374
389
 
375
390
  ### Статьи
@@ -377,7 +392,7 @@ YOUTRACK_TOKEN = "perm:your-token-here"
377
392
  | Tool | Описание | Основные параметры |
378
393
  | --- | --- | --- |
379
394
  | `article_get` | Получение статьи по ID. **Использование:** Чтение полного содержимого статьи базы знаний. **Возвращает:** предопределенные поля - id, idReadable, summary, content, contentPreview, usesMarkdown, parentArticle (id, idReadable), project (id, shortName, name) | `articleId` — ID статьи |
380
- | `article_list` | Список статей с фильтрами. **Использование:** Просмотр структуры базы знаний, навигация по статьям. **Возвращает:** предопределенные поля - id, idReadable, summary, usesMarkdown, parentArticle (id, idReadable), project (id, shortName, name). Поле content не включено для производительности | Опционально `parentArticleId` — фильтр по родительской статье, `projectId` — фильтр по проекту |
395
+ | `article_list` | Список статей с фильтрами и серверной пагинацией. **Возвращает:** предопределенные поля - id, idReadable, summary, usesMarkdown, parentArticle (id, idReadable), project (id, shortName, name). Поле content не включено для производительности | Опционально `parentArticleId` — фильтр по родительской статье, `projectId` — фильтр по проекту; `limit` (по умолчанию 100, макс 200), `skip` для пагинации |
381
396
  | `article_create` | Создание статьи в базе знаний. **Использование:** Создание документации, инструкций, статей поддержки. Поддерживает иерархическую структуру через `parentArticleId`. **Возвращает:** предопределенные поля созданной статьи - id, idReadable, summary, content, contentPreview, usesMarkdown, parentArticle, project | `summary` — заголовок статьи, опционально `content` — содержимое, `parentArticleId` — ID родительской статьи для создания подстатьи, `projectId` — ID проекта, `usesMarkdown` — использовать Markdown форматирование, `returnRendered` — вернуть отрендеренный preview |
382
397
  | `article_update` | Обновление существующей статьи. **Использование:** Редактирование содержимого, изменение заголовка статьи. **Возвращает:** предопределенные поля обновленной статьи | `articleId` — ID статьи, опционально `summary` — новый заголовок, `content` — новое содержимое, `usesMarkdown`, `returnRendered` |
383
398
  | `article_search` | Поиск статей в базе знаний по тексту. **Использование:** Полнотекстовый поиск по заголовкам и содержимому статей, поиск документации. Поддерживает фильтрацию по проекту и родительской статье. **Возвращает:** предопределенные поля - id, idReadable, summary, usesMarkdown, contentPreview (если `returnRendered=true`), parentArticle, project. Поле content не включено для производительности. **Ограничение:** минимум 2 символа в запросе, максимум 200 результатов | `query` — текст для поиска (минимум 2 символа), опционально `projectId` — фильтр по проекту, `parentArticleId` — фильтр по родительской статье, `limit` — максимум результатов (макс 200), `returnRendered` — вернуть отрендеренный preview содержимого; `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
@@ -400,4 +415,4 @@ YOUTRACK_TOKEN = "perm:your-token-here"
400
415
  | `issue_attachment_get` | Получение детальной информации о конкретном вложении. **Использование:** Проверка метаданных файла (размер, имя, тип, автор, дата). **Возвращает:** подробные метаданные вложения | `issueId` — код задачи, `attachmentId` — ID вложения; `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
401
416
  | `issue_attachment_download` | Получение URL для скачивания вложения или прямая загрузка файла в локальную файловую систему. **Использование:** Получение подписанного URL для загрузки файла или прямая загрузка файла в локальную файловую систему. **Возвращает:** метаданные вложения и подписанный URL, который можно использовать напрямую без дополнительной аутентификации, или информацию о загруженном файле | `issueId` — код задачи, `attachmentId` — ID вложения; `downloadToFile` — boolean, загрузить файл непосредственно в локальную файловую систему (по умолчанию: false); `downloadPath` — путь для сохранения файла (генерируется автоматически, если не указан); `overwrite` — разрешить перезапись существующих файлов (по умолчанию: false, выдает ошибку, если файл существует); `saveToFile`, `filePath`, `format`, `overwrite` — параметры сохранения файлов |
402
417
  | `issue_attachment_upload` | Загрузка файлов к задаче YouTrack. **Использование:** Прикрепление документов, скриншотов, логов к задаче. Файлы должны существовать в локальной файловой системе. **Примечание:** можно прикреплять файлы только к существующим задачам, не при создании. **Ограничение:** максимум 10 файлов за запрос | `issueId` — код задачи, `filePaths[]` — массив абсолютных путей к файлам (макс 10), опционально `muteUpdateNotifications` — не отправлять уведомления об обновлении |
403
- | `issue_attachment_delete` | Удаление вложения из задачи YouTrack. **ВАЖНО:** Требует явного подтверждения через параметр `confirmation` для предотвращения случайного удаления. **Использование:** Удаление устаревших или ошибочно загруженных файлов. Это деструктивная операция, которую нельзя отменить. | `issueId` — код задачи, `attachmentId` — ID вложения для удаления, `confirmation` — должен быть явно установлен в `true` для подтверждения удаления (обязательный параметр безопасности) |
418
+ | `issue_attachment_delete` | Удаление вложения из задачи YouTrack (требует `confirmation: true`). **Использование:** Удаление устаревших или ошибочно загруженных файлов. Это деструктивная операция, которую нельзя отменить. | `issueId`, `attachmentId`, `confirmation` — обязательно булев литерал `true` |
package/README.md CHANGED
@@ -36,12 +36,15 @@ MCP server for comprehensive YouTrack integration with the following capabilitie
36
36
  - [Setting up NPM\_TOKEN](#setting-up-npm_token)
37
37
  - [Release Process](#release-process)
38
38
  - [Manual Build \& Test](#manual-build--test)
39
+ - [Project Structure](#project-structure)
39
40
  - [Running the server (stdio)](#running-the-server-stdio)
40
41
  - [Configuration for Code (Recommended)](#configuration-for-code-recommended)
41
42
  - [Configuration for Claude Code CLI](#configuration-for-claude-code-cli)
42
43
  - [Configuration for VS Code Cline](#configuration-for-vs-code-cline)
43
44
  - [MCP Tools](#mcp-tools)
44
45
  - [File Storage Parameters](#file-storage-parameters)
46
+ - [Pagination](#pagination)
47
+ - [Destructive Operation Confirmation](#destructive-operation-confirmation)
45
48
  - [Service](#service)
46
49
  - [Issues](#issues)
47
50
  - [Issue Links](#issue-links)
@@ -58,7 +61,7 @@ MCP server for comprehensive YouTrack integration with the following capabilitie
58
61
 
59
62
  ## Requirements
60
63
 
61
- - Node.js ≥ 20
64
+ - Node.js ≥ 22
62
65
  - Environment variables:
63
66
  - `YOUTRACK_URL` — base URL of YouTrack instance
64
67
  - `YOUTRACK_TOKEN` — permanent token with read permissions for issues and work items
@@ -67,6 +70,8 @@ MCP server for comprehensive YouTrack integration with the following capabilitie
67
70
  - `YOUTRACK_PRE_HOLIDAYS` — optional comma-separated list of pre-holiday dates with reduced working hours
68
71
  - `YOUTRACK_USER_ALIASES` — optional comma-separated list of `alias:login` mappings (e.g., `me:vyt,petya:p.petrov`), used for automatic assignee selection
69
72
  - `YOUTRACK_DEFAULT_PROJECT` — optional project code used for manual verification tasks and default parent issues in docs/examples (use `PROJ` in documentation examples)
73
+ - `YOUTRACK_OUTPUT_DIR` — optional root directory for files produced by `saveToFile` and `downloadToFile`. Defaults to the current working directory. Absolute paths and parent-traversal segments (`..`) are rejected for safety
74
+ - `YOUTRACK_UPLOAD_DIR` — optional whitelist root for source files used by `issue_attachment_upload`. Defaults to `YOUTRACK_OUTPUT_DIR`. Paths whose realpath escapes this directory (including via symlinks) are rejected to prevent reading arbitrary files (`/etc/passwd`, `~/.ssh/id_rsa`, etc.)
70
75
 
71
76
 
72
77
  ## Installation
@@ -120,7 +125,7 @@ This project uses GitHub Actions for continuous integration and automated releas
120
125
 
121
126
  Runs automatically on every push and pull request:
122
127
  - **Triggers**: All branches, all pull requests
123
- - **Node.js versions**: 20.x, 22.x (matrix testing)
128
+ - **Node.js versions**: 22.x, 24.x (matrix testing)
124
129
  - **Steps**:
125
130
  1. Install dependencies (`npm ci`)
126
131
  2. Run linter (`npm run lint`)
@@ -131,7 +136,7 @@ Runs automatically on every push and pull request:
131
136
 
132
137
  Runs automatically when you create a new version tag:
133
138
  - **Trigger**: Git tags matching `v*` pattern (e.g., `v0.1.0`, `v1.2.3`)
134
- - **Node.js version**: 20.x
139
+ - **Node.js version**: 24.x
135
140
  - **Steps**:
136
141
  1. Install dependencies
137
142
  2. Build project
@@ -199,6 +204,29 @@ npm run lint
199
204
  npm run dev:watch
200
205
  ```
201
206
 
207
+ ## Project Structure
208
+
209
+ ```
210
+ src/
211
+ index.ts MCP server entry point (stdio transport)
212
+ server.ts MCP server bootstrap, tool registration
213
+ config.ts YOUTRACK_* environment variable parsing
214
+ constants.ts HTTP timeouts, body limits, PRE_HOLIDAY_RATIO
215
+ types.ts Shared TypeScript interfaces and YouTrack entity discriminators
216
+ youtrack-client.ts YouTrack REST client with caching and pagination helpers
217
+ tools/ MCP tool registrations (one file per domain: issues, articles, ...)
218
+ utils/ Reusable helpers (date, file-storage, mappers, validators, tool-args, ...)
219
+ __tests__/ Vitest tests against a nock-mocked HTTP layer
220
+ docs/
221
+ reviews/ Project-check review reports
222
+ .github/
223
+ workflows/ CI and publish workflows
224
+ dependabot.yml Weekly npm + github-actions update PRs
225
+ dist/ Build output (generated by `npm run build`, gitignored)
226
+ ```
227
+
228
+ The authoritative agent-oriented project layout lives in [AGENTS.md](AGENTS.md).
229
+
202
230
  ## Running the server (stdio)
203
231
 
204
232
  ```bash
@@ -283,11 +311,23 @@ To use this MCP server with [Cline](https://github.com/cline/cline) extension in
283
311
  Many tools support optional file storage parameters for handling large datasets:
284
312
 
285
313
  - `saveToFile` — boolean, saves results to a JSON file instead of returning directly (useful for large datasets)
286
- - `filePath` — string, custom file path (optional, auto-generated if not provided, directory created if needed)
314
+ - `filePath` — string, custom file path relative to `YOUTRACK_OUTPUT_DIR` (optional, auto-generated if not provided, directory created if needed)
287
315
  - `format` — string, output format when saving to file: `jsonl` (JSON Lines) or `json` (JSON array format). Default is `jsonl`
288
316
  - `overwrite` — boolean, allow overwriting existing files when using explicit filePath. Default is `false`
289
317
 
290
- When `saveToFile` is `true`, tools return metadata about the saved file instead of the full data.
318
+ When `saveToFile` is `true`, tools return metadata about the saved file instead of the full data. All paths are resolved against `YOUTRACK_OUTPUT_DIR`; absolute paths and `..` traversal segments are rejected.
319
+
320
+ ### Pagination
321
+
322
+ Most read tools accept `limit` (default 100, max 200) and `skip` (default 0); both are forwarded to YouTrack as `$top`/`$skip`. Tools that return aggregated counts use `returned` (count of items in the current page) rather than `total`. Use successive `skip` values to walk through large result sets.
323
+
324
+ ### Destructive Operation Confirmation
325
+
326
+ Tools that delete data require an explicit `confirmation: true` literal in their input:
327
+
328
+ - `issue_attachment_delete`
329
+ - `issue_link_delete`
330
+ - `workitem_delete`
291
331
 
292
332
  ### Service
293
333
 
@@ -303,7 +343,7 @@ When `saveToFile` is `true`, tools return metadata about the saved file instead
303
343
  | `issues_lookup` | Get information about multiple YouTrack issues (batch mode, max 50). Note: Returns predefined fields including timestamps (created, updated) and basic info - id, idReadable, summary, description, wikifiedDescription, usesMarkdown, created, updated, project (id, shortName, name), parent (id, idReadable), assignee (id, login, name), reporter (id, login, name), updater (id, login, name). By default, custom fields are not included. Use briefOutput=false to get all customFields including State. | `issueIds[]` — array of issue codes (e.g., ['PROJ-123', 'PROJ-124']), max 50; `briefOutput` — optional boolean (default `true`) |
304
344
  | `issue_details` | Issue details with brief/full modes | `issueId` — issue code; `briefOutput` — optional boolean (default `true`). Brief: predefined fields only. Full (`false`): adds `customFields` including `State`; `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
305
345
  | `issues_details` | Detailed information about multiple issues (batch mode, max 50). Brief (default): predefined fields only. Full (`briefOutput=false`): adds `customFields` for each issue | `issueIds[]` — array of issue codes, max 50; `briefOutput` — optional boolean (default `true`); `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
306
- | `issue_comments` | Issue comments | `issueId` — issue code; `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
346
+ | `issue_comments` | Issue comments with server-side pagination | `issueId` — issue code; `limit` (default 100, max 200), `skip` for pagination; `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
307
347
  | `issues_comments` | Comments for multiple issues (batch mode, max 50) | `issueIds[]` — array of issue codes, max 50; `briefOutput` — optional boolean (default `true`); `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
308
348
  | `issue_create` | Create issue | `projectId`, `summary`, optional `description`, `parentIssueId`, `assigneeLogin`, `stateName`, `usesMarkdown`, `links` (array of link objects) |
309
349
  | `issue_update` | Update existing issue | `issueId`, optionally `summary`, `description`, `parentIssueId` (empty string clears parent), `usesMarkdown` |
@@ -316,16 +356,16 @@ When `saveToFile` is `true`, tools return metadata about the saved file instead
316
356
  | `issue_attachment_get` | Get attachment info | `issueId`, `attachmentId`; `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
317
357
  | `issue_attachment_download` | Get download URL for attachment or download file directly to local filesystem | `issueId`, `attachmentId` — returns signed download URL when downloadToFile=false; `downloadToFile` — boolean, download file directly to local system (default: false); `downloadPath` — custom path to save file (auto-generated if not provided); `overwrite` — allow overwriting existing files (default: false, throws error if file exists); `saveToFile`, `filePath`, `format`, `overwrite` — file storage options for metadata |
318
358
  | `issue_attachment_upload` | Upload files to issue | `issueId`, `filePaths[]` — array of file paths (max 10), optionally `muteUpdateNotifications` |
319
- | `issue_attachment_delete` | Delete attachment (requires confirmation) | `issueId`, `attachmentId`, `confirmation` (must be `true`) |
359
+ | `issue_attachment_delete` | Delete attachment (requires `confirmation: true`) | `issueId`, `attachmentId`, `confirmation` (must be the boolean literal `true`) |
320
360
 
321
361
  ### Issue Links
322
362
 
323
363
  | Tool | Description | Main Parameters |
324
364
  | --- | --- | --- |
325
- | `issue_links` | List links for an issue (relates to, duplicate, parent/child). Returns link id, direction, linkType, and counterpart issue brief | `issueId` — issue code |
326
- | `issue_link_types` | List available link types | — |
365
+ | `issue_links` | List links for an issue with server-side pagination. Returns link id, direction, linkType, and counterpart issue brief | `issueId` — issue code; `limit` (default 100, max 200), `skip` for pagination |
366
+ | `issue_link_types` | List available link types (cached single-flight) | — |
327
367
  | `issue_link_add` | Create a link between two issues | `sourceId`, `targetId`, `linkType` (name or id), optionally `direction` (`outbound` or `inbound`) |
328
- | `issue_link_delete` | Delete a link by id for a specific issue | `issueId` issue code, `linkId` link id to delete |
368
+ | `issue_link_delete` | Delete a link by id for a specific issue (requires `confirmation: true`) | `issueId`, `linkId`, optionally `targetId`, `confirmation` (must be the boolean literal `true`) |
329
369
 
330
370
  ### Issues Status
331
371
 
@@ -348,14 +388,14 @@ When `saveToFile` is `true`, tools return metadata about the saved file instead
348
388
 
349
389
  | Tool | Description | Main Parameters |
350
390
  | --- | --- | --- |
351
- | `workitems_list` | Get work items for current or specified user | Optionally `issueId`, `author`, `startDate`, `endDate`, `allUsers`; `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
352
- | `workitems_all_users` | Get work items for all users | Optionally `issueId`, `startDate`, `endDate`; `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
353
- | `workitems_for_users` | Get work items for selected users | `users[]`, optionally `issueId`, `startDate`, `endDate`; `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
391
+ | `workitems_list` | Get work items for current or specified user with server-side pagination | Optionally `issueId`, `author`, `startDate`, `endDate`, `allUsers`; `limit` (default 100, max 200), `skip` for pagination; `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
392
+ | `workitems_all_users` | Get work items for all users with server-side pagination | Optionally `issueId`, `startDate`, `endDate`; `limit` (default 100, max 200), `skip` for pagination; `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
393
+ | `workitems_for_users` | Get work items for selected users with per-user pagination | `users[]`, optionally `issueId`, `startDate`, `endDate`; `limit` (default 100, max 200), `skip` applied per user; `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
354
394
  | `workitems_recent` | Get recent work items sorted by update time (newest first) | Optionally `users[]` (defaults to current user), `limit` (default 50, max 200); `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
355
395
  | `workitem_create` | Create work item entry | `issueId`, `date`, `minutes`, optionally `summary`, `description`, `usesMarkdown` |
356
396
  | `workitem_create_idempotent` | Create work item without duplicates (by description and date) | `issueId`, `date`, `minutes`, `description`, optionally `usesMarkdown` |
357
397
  | `workitem_update` | Update work item (recreate) | `issueId`, `workItemId`, optionally `date`, `minutes`, `summary`, `description`, `usesMarkdown` |
358
- | `workitem_delete` | Delete work item | `issueId`, `workItemId` |
398
+ | `workitem_delete` | Delete work item (requires `confirmation: true`) | `issueId`, `workItemId`, `confirmation` (must be the boolean literal `true`) |
359
399
  | `workitems_create_period` | Batch create for date range | `issueId`, `startDate`, `endDate`, `minutes`, optionally `summary`, `description`, `usesMarkdown`, `excludeWeekends`, `excludeHolidays`, `holidays[]`, `preHolidays[]` |
360
400
  | `workitems_report_summary` | Summary report for work items | Common parameters: `author`, `issueId`, `startDate`, `endDate`, `expectedDailyMinutes`, `excludeWeekends`, `excludeHolidays`, `holidays[]`, `preHolidays[]`, `allUsers`; `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
361
401
  | `workitems_report_invalid` | Days with deviation from expected hours | Same parameters as summary; `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
@@ -366,10 +406,10 @@ When `saveToFile` is `true`, tools return metadata about the saved file instead
366
406
 
367
407
  | Tool | Description | Main Parameters |
368
408
  | --- | --- | --- |
369
- | `users_list` | List all YouTrack users | `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
409
+ | `users_list` | List all YouTrack users with server-side pagination | `limit` (default 100, max 200), `skip` for pagination; `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
370
410
  | `user_get` | Get user by login | `login` — user login |
371
411
  | `user_current` | Get current authenticated user | — |
372
- | `projects_list` | List all YouTrack projects | |
412
+ | `projects_list` | List all YouTrack projects (auto-paginated by default; pass `limit`/`skip` for explicit paging) | Optional `limit` (max 200), `skip` |
373
413
  | `project_get` | Get project by short name | `shortName` — project short name |
374
414
 
375
415
  ### Articles
@@ -377,7 +417,7 @@ When `saveToFile` is `true`, tools return metadata about the saved file instead
377
417
  | Tool | Description | Main Parameters |
378
418
  | --- | --- | --- |
379
419
  | `article_get` | Get article by ID | `articleId` |
380
- | `article_list` | List articles with filters | Optionally `parentArticleId`, `projectId` |
420
+ | `article_list` | List articles with filters and server-side pagination | Optionally `parentArticleId`, `projectId`; `limit` (default 100, max 200), `skip` for pagination |
381
421
  | `article_create` | Create article in knowledge base | `summary`, optionally `content`, `parentArticleId`, `projectId`, `usesMarkdown`, `returnRendered` |
382
422
  | `article_update` | Update article | `articleId`, optionally `summary`, `content`, `usesMarkdown`, `returnRendered` |
383
423
  | `article_search` | Search articles in knowledge base | `query`, optionally `projectId`, `parentArticleId`, `limit`, `returnRendered`; `saveToFile`, `filePath`, `format`, `overwrite` — file storage options |
@@ -403,9 +443,11 @@ When `saveToFile` is `true`, tools return metadata about the saved file instead
403
443
 
404
444
  ### Destructive Operations
405
445
 
406
- Some operations cannot be undone and require explicit confirmation:
446
+ Some operations cannot be undone and require an explicit `confirmation: true` literal in their input:
407
447
 
408
- - **`issue_attachment_delete`** - Requires `confirmation: true` parameter. Deleted attachments cannot be recovered.
448
+ - **`issue_attachment_delete`** deleted attachments cannot be recovered.
449
+ - **`issue_link_delete`** — re-fetch `issue_links` afterwards to verify the relationship was removed.
450
+ - **`workitem_delete`** — work item entries are permanently lost; re-fetch `workitems_list` to confirm.
409
451
  **issue_create parameters:**
410
452
 
411
453
  - `parentIssueId` — resolves against `YOUTRACK_DEFAULT_PROJECT` when no project prefix is provided (e.g., `123` → `BC-123` if default project is `BC`).
package/dist/package.json CHANGED
@@ -1,15 +1,20 @@
1
1
  {
2
2
  "name": "@vitalyostanin/youtrack-mcp",
3
- "version": "0.10.2",
3
+ "version": "0.12.0",
4
4
  "scripts": {
5
5
  "build": "tsc -p tsconfig.json",
6
6
  "postbuild": "chmod +x dist/index.js",
7
7
  "dev": "tsc -p tsconfig.json --watch",
8
8
  "start": "node dist/index.js",
9
9
  "dev:watch": "tsc -p tsconfig.json --watch",
10
- "lint": "eslint .",
11
- "lint:fix": "eslint . --fix",
12
- "prepublishOnly": "npm run build"
10
+ "lint": "eslint . --cache --cache-location node_modules/.cache/eslint/",
11
+ "lint:fix": "eslint . --fix --cache --cache-location node_modules/.cache/eslint/",
12
+ "format": "eslint . --fix --cache --cache-location node_modules/.cache/eslint/",
13
+ "typecheck": "tsc -p tsconfig.json --noEmit",
14
+ "test": "vitest run",
15
+ "test:watch": "vitest",
16
+ "test:coverage": "vitest run --coverage",
17
+ "prepublishOnly": "npm run lint && npm test && npm run build"
13
18
  },
14
19
  "keywords": [
15
20
  "mcp",
@@ -26,7 +31,7 @@
26
31
  "description": "MCP server for comprehensive YouTrack integration: issues, work items with reports, user activity search, knowledge base, projects and users",
27
32
  "type": "module",
28
33
  "engines": {
29
- "node": ">=20.0.0"
34
+ "node": ">=22.0.0"
30
35
  },
31
36
  "bin": {
32
37
  "youtrack-mcp": "dist/index.js"
@@ -50,21 +55,25 @@
50
55
  "access": "public"
51
56
  },
52
57
  "dependencies": {
53
- "@modelcontextprotocol/sdk": "^1.20.0",
58
+ "@modelcontextprotocol/sdk": "^1.29.0",
54
59
  "@vitalyostanin/mutex-pool": "^0.0.3",
55
- "axios": "^1.12.2",
60
+ "axios": "^1.16.0",
56
61
  "form-data": "^4.0.1",
57
62
  "luxon": "^3.5.0",
63
+ "stream-json": "^2.1.0",
58
64
  "zod": "^3.25.76"
59
65
  },
60
66
  "devDependencies": {
61
67
  "@eslint/js": "^9.37.0",
62
68
  "@types/luxon": "^3.7.1",
63
- "@types/node": "^22.18.10",
69
+ "@types/node": "^22.19.17",
64
70
  "@vitalyostanin/eslint-prefer-de-morgan-law": "^1.0.2",
71
+ "@vitest/coverage-v8": "^4.1.5",
65
72
  "eslint": "^9.37.0",
73
+ "nock": "^14.0.14",
66
74
  "typescript": "^5.6.2",
67
75
  "typescript-eslint": "^8.46.0",
76
+ "vitest": "^4.1.5",
68
77
  "zod-to-json-schema": "^3.24.6"
69
78
  }
70
79
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=config.test.d.ts.map
@@ -0,0 +1,47 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync, rmSync, existsSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join, resolve } from "node:path";
5
+ import { loadConfig, enrichConfigWithRedaction } from "../config.js";
6
+ describe("loadConfig", () => {
7
+ let tmp;
8
+ const oldEnv = { ...process.env };
9
+ beforeEach(() => {
10
+ tmp = mkdtempSync(join(tmpdir(), "yt-cfg-"));
11
+ process.env = {
12
+ YOUTRACK_URL: "https://yt.example.com",
13
+ YOUTRACK_TOKEN: "perm:test-token",
14
+ };
15
+ });
16
+ afterEach(() => {
17
+ rmSync(tmp, { recursive: true, force: true });
18
+ process.env = { ...oldEnv };
19
+ });
20
+ it("uses cwd by default for outputDir", () => {
21
+ const cfg = loadConfig();
22
+ expect(cfg.outputDir).toBe(resolve(process.cwd()));
23
+ });
24
+ it("honors YOUTRACK_OUTPUT_DIR", () => {
25
+ process.env.YOUTRACK_OUTPUT_DIR = tmp;
26
+ const cfg = loadConfig();
27
+ expect(cfg.outputDir).toBe(resolve(tmp));
28
+ });
29
+ it("creates outputDir if missing", () => {
30
+ const sub = join(tmp, "missing-subdir");
31
+ process.env.YOUTRACK_OUTPUT_DIR = sub;
32
+ loadConfig();
33
+ expect(existsSync(sub)).toBe(true);
34
+ });
35
+ it("throws when YOUTRACK_TOKEN is missing", () => {
36
+ delete process.env.YOUTRACK_TOKEN;
37
+ expect(() => loadConfig()).toThrow();
38
+ });
39
+ it("redacts token via enrichConfigWithRedaction", () => {
40
+ const cfg = loadConfig();
41
+ const redacted = enrichConfigWithRedaction(cfg);
42
+ expect(redacted.hasToken).toBe(true);
43
+ expect(redacted.token).toBeUndefined();
44
+ expect(redacted.outputDir).toBe(cfg.outputDir);
45
+ });
46
+ });
47
+ //# sourceMappingURL=config.test.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1,12 @@
1
+ import { afterAll, afterEach, beforeAll } from "vitest";
2
+ import nock from "nock";
3
+ beforeAll(() => {
4
+ nock.disableNetConnect();
5
+ });
6
+ afterEach(() => {
7
+ nock.cleanAll();
8
+ });
9
+ afterAll(() => {
10
+ nock.enableNetConnect();
11
+ });
12
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=smoke.test.d.ts.map
@@ -0,0 +1,7 @@
1
+ import { describe, it, expect } from "vitest";
2
+ describe("smoke", () => {
3
+ it("vitest is wired", () => {
4
+ expect(1 + 1).toBe(2);
5
+ });
6
+ });
7
+ //# sourceMappingURL=smoke.test.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=youtrack-client.delete-link.test.d.ts.map
@@ -0,0 +1,93 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { YoutrackClient } from "../youtrack-client.js";
3
+ const config = {
4
+ baseUrl: "https://yt.test",
5
+ token: "perm:test",
6
+ defaultProject: "BC",
7
+ outputDir: "/tmp",
8
+ timezone: "UTC",
9
+ };
10
+ function getHttp(client) {
11
+ return client.http;
12
+ }
13
+ function methodNotAllowed() {
14
+ return {
15
+ isAxiosError: true,
16
+ message: "Request failed with status code 405",
17
+ response: { status: 405, data: { error: "method_not_allowed" } },
18
+ };
19
+ }
20
+ describe("deleteIssueLink subtask fallback", () => {
21
+ it("uses linkToDelete.issue.idReadable when targetId is missing", async () => {
22
+ const client = new YoutrackClient(config);
23
+ const http = getHttp(client);
24
+ vi.spyOn(http, "delete").mockRejectedValue(methodNotAllowed());
25
+ vi.spyOn(client, "getIssueLinks").mockResolvedValue({
26
+ issueId: "BC-1",
27
+ links: [
28
+ {
29
+ id: "L1",
30
+ direction: "OUTWARD",
31
+ linkType: { name: "Subtask", id: "lt-sub" },
32
+ issue: { idReadable: "BC-99", id: "1-99", summary: "s" },
33
+ },
34
+ ],
35
+ });
36
+ let captured;
37
+ vi.spyOn(http, "post").mockImplementation((...args) => {
38
+ captured = { url: String(args[0]), body: args[1] };
39
+ return Promise.resolve({ data: {} });
40
+ });
41
+ await client.deleteIssueLink({ issueId: "BC-1", linkId: "L1" });
42
+ expect(captured?.url).toBe("/api/commands");
43
+ expect(captured?.body.query).toBe("remove subtask of BC-99");
44
+ const issues = captured?.body.issues;
45
+ expect(issues[0].idReadable).toBe("BC-1");
46
+ });
47
+ it("uses provided targetId when given (regular link)", async () => {
48
+ const client = new YoutrackClient(config);
49
+ const http = getHttp(client);
50
+ vi.spyOn(http, "delete").mockRejectedValue(methodNotAllowed());
51
+ vi.spyOn(client, "getIssueLinks").mockResolvedValue({
52
+ issueId: "BC-1",
53
+ links: [
54
+ {
55
+ id: "L1",
56
+ direction: "OUTWARD",
57
+ linkType: {
58
+ name: "Relates",
59
+ id: "lt-rel",
60
+ outwardName: "relates to",
61
+ inwardName: "relates to",
62
+ },
63
+ issue: { idReadable: "BC-99", id: "1-99", summary: "s" },
64
+ },
65
+ ],
66
+ });
67
+ let captured;
68
+ vi.spyOn(http, "post").mockImplementation((...args) => {
69
+ captured = { body: args[1] };
70
+ return Promise.resolve({ data: {} });
71
+ });
72
+ await client.deleteIssueLink({ issueId: "BC-1", linkId: "L1", targetId: "BC-7" });
73
+ expect(String(captured?.body.query)).toContain("BC-7");
74
+ });
75
+ it("throws when neither input.targetId nor link payload provide a target", async () => {
76
+ const client = new YoutrackClient(config);
77
+ const http = getHttp(client);
78
+ vi.spyOn(http, "delete").mockRejectedValue(methodNotAllowed());
79
+ vi.spyOn(client, "getIssueLinks").mockResolvedValue({
80
+ issueId: "BC-1",
81
+ links: [
82
+ {
83
+ id: "L1",
84
+ direction: "OUTWARD",
85
+ linkType: { name: "Subtask", id: "lt-sub" },
86
+ issue: { idReadable: "", id: "1-99", summary: "s" },
87
+ },
88
+ ],
89
+ });
90
+ await expect(client.deleteIssueLink({ issueId: "BC-1", linkId: "L1" })).rejects.toThrow(/Cannot determine target issue id/);
91
+ });
92
+ });
93
+ //# sourceMappingURL=youtrack-client.delete-link.test.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=youtrack-client.normalize-error.test.d.ts.map