Metadata-Version: 2.5
Name: ktalk-mcp
Version: 0.9.1
Summary: MCP server for accessing Kontur Talk (KTalk) recordings, transcripts and summaries
Project-URL: Homepage, https://github.com/mdemyanov/ktalk-mcp
Project-URL: Repository, https://github.com/mdemyanov/ktalk-mcp
Project-URL: Issues, https://github.com/mdemyanov/ktalk-mcp/issues
Author-email: Maksim Demyanov <mdemyanov@users.noreply.github.com>
License-Expression: MIT
License-File: LICENSE
Keywords: kontur,ktalk,mcp,recordings,transcripts
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications :: Conferencing
Requires-Python: >=3.12
Requires-Dist: fastmcp>=2.0.0
Requires-Dist: httpx>=0.28.0
Requires-Dist: pydantic-settings>=2.0.0
Description-Content-Type: text/markdown

# ktalk-mcp

[![PyPI](https://img.shields.io/pypi/v/ktalk-mcp)](https://pypi.org/project/ktalk-mcp/)
[![Python](https://img.shields.io/pypi/pyversions/ktalk-mcp)](https://pypi.org/project/ktalk-mcp/)

MCP сервер для доступа к записям [Контур.Толк](https://ktalk.ru) (KTalk) из Claude Code.

Предоставляет доступ к:
- Списку записей конференций
- Деталям записи
- Транскриптам (распознанная речь по спикерам)
- Саммари и протоколам встреч
- Полному составу участников записи (обходит лимит в 6 участников в списковом ответе)
- Скачиванию видеофайла записи
- Архиву встреч и истории чата (доступно только с персональным API-ключом)
- Конфигурации комнаты — политики аудио/видео/демонстрации, модераторы, SIP, чат,
  маскирование (доступно только в режиме session token)
- Календарю запланированных встреч, видимых активной авторизации (доступно только
  в режиме session token)
- Предпросмотру новой встречи без её создания — само создание сделано намеренно
  недоступным агенту, см. «Планирование встречи» ниже
- Диагностике авторизации — какой ключ/токен активен и почему запрос не проходит

## Установка

Требуется Python 3.12+ и [uv](https://docs.astral.sh/uv/).

```bash
uv tool install ktalk-mcp
```

Или через pip:

```bash
pip install ktalk-mcp
```

## Авторизация

Сервер поддерживает два способа авторизации: session token (кука браузера) и
персональный API-ключ. Способы исключают друг друга: если задать обе переменные,
побеждает `KTALK_PERSONAL_API_KEY` — `KTALK_SESSION_TOKEN` в этом случае вообще не
читается. Не задать ни одну — сервер завершится понятной ошибкой при старте.

Персональный API-ключ не привязан к браузерной сессии и не протухает без предупреждения,
в отличие от session token. Берите его, если сервер должен работать стабильно, а не
только для разового запроса.

### Session token

Session token — значение из cookie браузерной сессии Толка. Передаётся как
query-параметр `sessionToken`. Быстрый способ начать, но токен живёт недолго и
протухает без предупреждения — при регулярном использовании удобнее персональный
API-ключ (ниже).

1. Откройте https://your-domain.ktalk.ru в браузере
2. Войдите в свой аккаунт
3. Откройте DevTools: нажмите `F12` (или `Cmd+Option+I` на Mac)
4. Перейдите во вкладку **Application** → **Cookies** → `https://your-domain.ktalk.ru`
5. Найдите cookie с именем `sessionToken`
6. Скопируйте его значение

> **Важно:** session token имеет ограниченный срок жизни. Если MCP tool возвращает ошибку авторизации, получите новый токен по инструкции выше.

### Персональный API-ключ

Персональный API-ключ выдаётся в админке Толка на конкретного пользователя на
настраиваемый срок и не зависит от того, открыт ли браузер. Передаётся заголовком
`X-Auth-Token`, а не в URL — секрет не попадает в query-параметры и логи веб-сервера.

Выпускается и ротируется в разделе **Управление → API-ключи** админки Толка (UI-шаг,
CLI-эквивалента нет; экранные шаги здесь не расписываем — актуальный порядок действий
смотрите в справке Контура:
[«Персональный API-ключ доступа в Толке»](https://support.kontur.ru/talk/86797)).
Значение ключа показывается один раз в течение часа после создания — не скопировали
вовремя, придётся выпускать новый.

**Не путайте с ключом пространства.** В Толке есть второй, отдельный ключ —
пространственный, с заголовком `X-API-Key`, выдаётся не на пользователя, а на всё
пространство целиком. `ktalk-mcp` работает только с персональным ключом
(`X-Auth-Token`); ключ пространства не поддерживается — переменная называется
`KTALK_PERSONAL_API_KEY`, а не `KTALK_API_KEY`, намеренно, чтобы их не перепутать.

При выпуске ключа в админке выбираются права (scope). Не хватает прав — запрос вернёт
403, и по виду это неотличимо от «ключ невалиден», хотя ключ рабочий (подробнее —
«Диагностика авторизации» ниже).

| Право (scope) | Даёт доступ к |
|---|---|
| `application.recording.read` | Список записей, детали, транскрипт, саммари, скачивание файла, участники |
| `application.reporting.read` | Архив встреч, чат встречи, отчёты по участникам |
| `application.applications.read` | Опционально. Без него `ktalk_auth_status` / `ktalk auth-status` не покажет состав прав и срок действия ключа — только «ключ живой / не живой» |

Ротация: после перевыпуска ключа в админке обновите значение `KTALK_PERSONAL_API_KEY` в
конфигурации (`.mcp.json` или переменной окружения, см. ниже) и перезапустите MCP сервер.

> **Если реестр `ktalk` уже накопил записи в session-режиме,** перед первым `ktalk sync`
> после переключения на персональный ключ обязательно выполните `ktalk sync --dry-run`.
> Внутренний и официальный контуры API отдают идентификаторы записей по-разному, и без
> сверки первый боевой sync под ключом рискует задвоить весь реестр. Команда только
> сверяет id и ничего не пишет — см. таблицу команд CLI ниже.

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

Добавьте в файл `~/.claude/.mcp.json` (глобально) или `.mcp.json` (в проекте).

С персональным API-ключом:

```json
{
  "mcpServers": {
    "ktalk": {
      "command": "uvx",
      "args": ["ktalk-mcp"],
      "env": {
        "KTALK_PERSONAL_API_KEY": "ваш_персональный_api_ключ",
        "KTALK_BASE_URL": "https://your-domain.ktalk.ru"
      }
    }
  }
}
```

С session token:

```json
{
  "mcpServers": {
    "ktalk": {
      "command": "uvx",
      "args": ["ktalk-mcp"],
      "env": {
        "KTALK_SESSION_TOKEN": "ваш_session_token",
        "KTALK_BASE_URL": "https://your-domain.ktalk.ru"
      }
    }
  }
}
```

### Альтернативная конфигурация

Переменные окружения можно задать отдельно (выберите одну из двух):

```bash
export KTALK_PERSONAL_API_KEY="ваш_персональный_api_ключ"
# или
export KTALK_SESSION_TOKEN="ваш_session_token"
export KTALK_BASE_URL="https://your-domain.ktalk.ru"
```

Также поддерживается файл `.env` в рабочей директории:

```env
KTALK_PERSONAL_API_KEY=ваш_персональный_api_ключ
KTALK_BASE_URL=https://your-domain.ktalk.ru
```

## Диагностика авторизации

Проверьте авторизацию без запроса записей — MCP tool `ktalk_auth_status` в Claude Code
или CLI-команда:

```bash
uv run ktalk auth-status
```

Диагностика различает два случая, которые снаружи выглядят одинаково — просто ошибка, —
но чинятся по-разному:

- **401** — ключ или токен невалиден либо истёк. Перевыпустите его.
- **403** — ключ рабочий, но конкретному запросу не хватает прав (scope). Отредактируйте
  права ключа в админке Толка (см. таблицу в разделе «Персональный API-ключ» выше) —
  перевыпускать ключ не нужно.

У session token понятия scope нет — диагностика в этом режиме пробным запросом списка
записей сообщает только «токен работает / не работает», без прав и срока действия.

Режим ключа не проверен полностью на боевом окружении — команда описывает задуманное
поведение, а не гарантию для любого ключа.

## Доступные MCP Tools

### `ktalk_list_recordings`

Список записей конференций.

| Параметр | Тип | Default | Описание |
|----------|-----|---------|----------|
| `query` | str | — | Поиск по названию, комнате, автору |
| `start_from` | str | — | Начало периода (ISO 8601) |
| `start_to` | str | — | Конец периода |
| `top` | int | 30 | Количество записей (1–1000) |
| `order` | str | byTimeNewFirst | Сортировка: `byTimeNewFirst`, `byTimeOldFirst`, `byTitle`, `bySizeBigFirst`, `bySizeSmallFirst` |
| `page_token` | str | — | Токен пагинации |
| `format` | str | markdown | raw / markdown |

### `ktalk_get_recording`

Детали одной записи — автор, дата, длительность, список участников.

| Параметр | Тип | Default | Описание |
|----------|-----|---------|----------|
| `recording_key` | str | — | Ключ (ID) записи |
| `format` | str | markdown | raw / markdown |

### `ktalk_get_transcript`

Транскрипт записи — распознанная речь по спикерам с таймкодами.

Поддерживает **чанкинг** для длинных транскриптов: при превышении `chunk_size` ответ автоматически разбивается на части по границам реплик (не в середине фразы). Каждый чанк содержит метаданные для постраничного чтения.

| Параметр | Тип | Default | Описание |
|----------|-----|---------|----------|
| `recording_key` | str | — | Ключ (ID) записи |
| `format` | str | markdown | raw / markdown |
| `chunk` | int | 0 | Номер чанка. 0 = авто (целиком если маленький, первый чанк если большой). 1+ = конкретный чанк |
| `chunk_size` | int | 30000 | Макс. символов в чанке (~7500 токенов). Мягкий лимит — разрез по границам реплик |

### `ktalk_get_summary`

Полное саммари записи (краткое резюме + протокол).

| Параметр | Тип | Default | Описание |
|----------|-----|---------|----------|
| `recording_key` | str | — | Ключ (ID) записи |
| `format` | str | markdown | raw / markdown |

### `ktalk_get_summary_by_type`

Саммари конкретного типа.

| Параметр | Тип | Default | Описание |
|----------|-----|---------|----------|
| `recording_key` | str | — | Ключ (ID) записи |
| `summary_type` | str | — | `shortSummary` / `protocol` |
| `format` | str | markdown | raw / markdown |

### `ktalk_get_participants`

Полный состав участников записи. В отличие от списка/деталей записи (там результат
ограничен `maxParticipantCount`, по умолчанию не больше 6), дообогащает результат
отдельными запросами, включая анонимных участников.

| Параметр | Тип | Default | Описание |
|----------|-----|---------|----------|
| `recording_key` | str | — | Ключ (ID) записи |
| `format` | str | markdown | raw / markdown |

### `ktalk_download_recording`

Скачивает видеофайл записи на диск потоково, без буферизации целиком в памяти.

| Параметр | Тип | Default | Описание |
|----------|-----|---------|----------|
| `recording_key` | str | — | Ключ (ID) записи |
| `target_path` | str | — | Путь на диске для сохранения файла. Родительские директории создаются; существующий файл не перезаписывается |
| `quality` | str | — | Качество видео, например `900p`. Не указано — выбирается качество по умолчанию из доступных для записи |
| `format` | str | markdown | Формат возвращаемых метаданных о скачивании — raw / markdown |

### `ktalk_list_archive`

Архив встреч за период. Доступен только в режиме персонального API-ключа (право
`application.reporting.read`). Читает всё окно дат на стороне клиента и возвращает
результат одним вызовом, без постраничного чтения.

| Параметр | Тип | Default | Описание |
|----------|-----|---------|----------|
| `from_date` | str | — | Начало периода (ISO 8601) |
| `to_date` | str | — | Конец периода (ISO 8601) |
| `room_names` | list[str] | — | Фильтр по названиям комнат |
| `format` | str | markdown | raw / markdown |

### `ktalk_get_chat_messages`

Сообщения чата встречи. Нужен `recording_key` или `conference_key`; если канал не
указан, клиент сам определяет доступный канал вместо ошибки «channel field is
required».

| Параметр | Тип | Default | Описание |
|----------|-----|---------|----------|
| `recording_key` | str | — | Ключ записи — используется, чтобы определить встречу |
| `conference_key` | str | — | Ключ встречи — используется напрямую, если указан |
| `channel` | str | — | Имя канала чата, например `general`. Не указано — определяется автоматически |
| `format` | str | markdown | raw / markdown |

Одно из `recording_key` / `conference_key` обязательно.

### `ktalk_get_room`

Конфигурация комнаты по имени: политики аудио/видео/демонстрации экрана, модераторы,
анонимный доступ, SIP, чат, маскирование, залы сессий. Доступно только в режиме
session token — в режиме персонального ключа отказывает намеренно, так как этот путь
на api-key ни разу не подтверждён.

| Параметр | Тип | Default | Описание |
|----------|-----|---------|----------|
| `room_name` | str | — | Имя комнаты |
| `format` | str | markdown | raw / markdown |

### `ktalk_list_calendar`

Запланированные встречи за окно дат, видимые активной авторизации. Доступно только
в режиме session token.

Это **не «ваш личный календарь»** — выдача покрывает всё, что видит текущая
авторизация, включая чужие встречи. Сервер ограничивает один запрос семью днями;
инструмент сам режет произвольное окно на такие сегменты, от вас это не требует
никаких действий. На один сегмент возвращается не больше 100 встреч, и добрать
остаток нечем — если сегмент упёрся в этот потолок, ответ явно предупреждает, что
выдача по нему может быть неполной.

| Параметр | Тип | Default | Описание |
|----------|-----|---------|----------|
| `start` | str | — | Начало окна (ISO 8601), обязателен |
| `end` | str | — | Конец окна (ISO 8601), обязателен |
| `room_name` | str | — | Фильтр по названию комнаты |
| `format` | str | markdown | raw / markdown |

### `ktalk_preview_meeting`

Предпросмотр встречи, которая могла бы быть создана — без единого сетевого запроса.
Ничего не создаёт и не может создать: у MCP-сервера нет ни одного инструмента,
который бы создавал встречу. Подробности и причина — раздел «Планирование встречи»
ниже.

| Параметр | Тип | Default | Описание |
|----------|-----|---------|----------|
| `subject` | str | — | Тема встречи. Обязателен |
| `start` | str | — | Начало (ISO 8601). Обязателен |
| `end` | str | — | Конец (ISO 8601). Обязателен |
| `timezone` | str | — | Часовой пояс. Обязателен — без тихого умолчания |
| `room_name` | str | — | Комната. Обязателен |
| `required_user_keys` | list[str] | — | Обязательные участники. Явный пустой список — валидное значение |
| `description` | str | "" | Описание. Единственное поле с тихим дефолтом |
| `enable_auto_recording` | bool | — | Автозапись встречи. Обязателен |
| `enable_sip` | bool | — | SIP-подключение. Обязателен |
| `pin_code` | str | — | PIN комнаты. Явная пустая строка — валидное «без PIN» |
| `allow_anonymous` | bool | — | Доступ неавторизованных участников. Обязателен |
| `format` | str | markdown | raw / markdown |

Любое из обязательных полей, переданное как отсутствующее, — отказ до сетевого
вызова, с указанием, какого именно поля не хватает.

### `ktalk_auth_status`

Диагностика активного механизма авторизации — какой режим активен, жив ли ключ/токен,
какие права у ключа. Подробности — в разделе «Диагностика авторизации» выше.

| Параметр | Тип | Default | Описание |
|----------|-----|---------|----------|
| `format` | str | markdown | raw / markdown |

## Планирование встречи

Создание встречи — единственная операция пакета, которая что-то меняет вне вашего
компьютера: она рассылает приглашения реальным людям. Удаление созданного события
эти письма не отзывает. Из-за этого создание устроено умышленно неудобно:

- **MCP-агенту создание недоступно вовсе.** Ни в Claude Code, ни в любом другом
  MCP-клиенте нет инструмента, который создаёт встречу — только предпросмотр,
  `ktalk_preview_meeting` (см. выше).
- Само создание — команда CLI `ktalk create-meeting-confirm`. Она работает только
  в интерактивном терминале (проверяет, что и ввод, и вывод — реальный TTY) и перед
  отправкой печатает предпросмотр и требует набрать слово `да`.
- Предпросмотр без создания доступен и в CLI: `ktalk create-meeting-preview` — не
  делает ни одного сетевого запроса.
- Обе команды работают только в режиме session token — в режиме персонального
  ключа создание встречи не подтверждено ни разу и потому отключено.

**Ни одно поле не имеет значения по умолчанию** (кроме описания встречи — пустая
строка, если не задано). Тема, начало, конец, часовой пояс, комната, участники,
анонимный доступ, PIN, SIP, автозапись — каждое нужно передать явно; иначе команда
откажет и назовёт, какого поля не хватает. Так сделано намеренно: молчаливый
часовой пояс сдвинет встречу в календаре участников на другое время, а молчаливый
SIP или автозапись незаметно для организатора изменят, кто может подключиться
и записывается ли встреча.

Из этого вытекают два практических следствия:

- Булевы флаги (`--enable-sip`, `--enable-auto-recording`, `--allow-anonymous`)
  принимают только явные `true` или `false` — «флаг просто не указан» не считается
  ответом.
- «Встреча без обязательных участников» — это отдельный флаг `--no-required-users`,
  а не просто отсутствие `--required-user-key`. Отсутствие без флага трактуется как
  «вопрос не решён», а не как «участников нет».

Повторяющиеся встречи в этой версии не поддерживаются — можно создать только
разовое событие.

При сетевом сбое во время создания команда не повторяет запрос сама: если сеть
оборвалась, неизвестно, ушло приглашение или нет, и автоматический повтор рискует
создать дубль. Решение о повторной попытке — за вами; перед ней стоит проверить
`ktalk_list_calendar`, не появилась ли встреча уже.

Создание встречи ещё ни разу не выполнялось на боевом окружении — команда
реализует задуманное поведение, но не проверена живым вызовом.

```bash
# Предпросмотр — без сети, без побочных эффектов
uv run ktalk create-meeting-preview \
  --subject "Синк по проекту" \
  --start 2026-08-20T10:00:00 --end 2026-08-20T10:30:00 --timezone Europe/Moscow \
  --room-name "Переговорная 1" \
  --no-required-users \
  --enable-sip false --enable-auto-recording false --allow-anonymous false \
  --pin-code ""

# Создание — только в интерактивном терминале, требует ввода "да"
uv run ktalk create-meeting-confirm \
  --subject "Синк по проекту" \
  --start 2026-08-20T10:00:00 --end 2026-08-20T10:30:00 --timezone Europe/Moscow \
  --room-name "Переговорная 1" \
  --required-user-key user-123 --required-user-key user-456 \
  --enable-sip false --enable-auto-recording false --allow-anonymous false \
  --pin-code ""
```

## API

Сервер работает с KTalk Web API. Набор путей, которые вызывает клиент, зависит от
активного режима авторизации (см. «Авторизация» выше):

- **Session-режим** — авторизация query-параметром `sessionToken`, используется
  внутренний контур API.
- **Режим персонального ключа** — авторизация заголовком `X-Auth-Token`, используются
  официальные пути интеграторского API (`talk.public.api-api-2.json`).

Транскрипт и саммари используют один и тот же путь в обоих режимах:

| Эндпоинт | Описание |
|----------|----------|
| `GET /api/recordings/{id}/transcript` | Транскрипт |
| `GET /api/recordings/v2/{id}/summary` | Полное саммари (v2) |
| `GET /api/recordings/{id}/summary/{type}` | Саммари по типу |

Список записей и детали записи используют разные пути в session- и api-key-режимах.
Архив встреч, чат, полный состав участников, скачивание файла и диагностика ключа
доступны только в режиме персонального ключа (нужные права — в таблице раздела
«Персональный API-ключ» выше).

Комната, календарь и создание встречи работают только в режиме session token — в
режиме персонального ключа эти операции отказывают осознанно, а не по случайному
пробелу: путь на api-key либо не подтверждён вовсе, либо ведёт себя необъяснимо
непоследовательно при проверке.

> OpenAPI спецификация `talk.public.api-api-2.json` включена как справочник, но содержит расхождения с реальным API (пути, формат авторизации, структура ответов).

## CLI реестра (`ktalk`)

Тот же пакет ставит вторую команду — `ktalk`, операционный реестр записей на
SQLite. Вся детерминированная механика (синхронизация списка записей, дедуп,
экспирация, смена статусов, рендер дашборда и markdown-зеркала, разовая
миграция) живёт в коде, а не в рассуждениях модели.

**SQLite — операционный source of truth.** Markdown-файл `registry.md` —
генерируемое read-only зеркало для git (`ktalk export`), руками не редактируется.

Путь к базе: флаг `--db PATH` > переменная `KTALK_REGISTRY_DB` > дефолт
`95_TRANSCRIPTS/.registry.db` (относительно текущего каталога). Бинарную БД
нужно добавить в `.gitignore` (`.registry.db`, `.registry.db-wal`, `.registry.db-shm`).

`ktalk auth-status`, `ktalk create-meeting-preview` и `ktalk create-meeting-confirm`
реестр не открывают вовсе — им он не нужен. В частности, `auth-status` теперь
работает даже если файла базы данных нет или он недоступен: раньше команда
падала с ошибкой открытия БД, хотя для диагностики авторизации она не требуется.
Планирование встречи — отдельный раздел «Планирование встречи» выше.

| Команда | Назначение |
|---|---|
| `ktalk sync [--days 7] [--json] [--dry-run]` | Загрузить записи из KTalk, upsert новых (`new`), экспирировать `new` старше N дней → `skipped`, показать дашборд. Идемпотентно. `--dry-run` — сверить id с реестром без записи, ничего не пишет (обязателен перед первым `sync` в режиме персонального ключа — см. «Персональный API-ключ»). |
| `ktalk auth-status [--json]` | Диагностика активной авторизации — жив ли ключ/токен, какие права у ключа. См. «Диагностика авторизации». |
| `ktalk dashboard [--json]` | Дашборд: новые записи, статистика по статусам. |
| `ktalk list [--status S] [--json]` | Список записей с фильтром по статусу. |
| `ktalk show <id> [--json]` | Детали записи: участники, статус, пути, длительность. |
| `ktalk mark-processing <id>` | Перевести в `processing`. |
| `ktalk mark-done <id> --transcript P --protocol P [--type T]` | Завершить, проставить пути и `processed_at`. |
| `ktalk mark-partial <id> [--transcript P] [--protocol P]` | Частичная обработка. |
| `ktalk mark-skipped <id>` | Пропустить вручную. |
| `ktalk set-vault-id <id> <ktalk_id> <vault_id>` | Привязать профиль к участнику. |
| `ktalk export [--out PATH] [--full]` | Сгенерировать markdown-зеркало. |
| `ktalk migrate <vault> [--dry-run] [--json]` | Разовый импорт из markdown-реестров. |

Все команды поддерживают `--json` (валидный JSON в stdout; ошибки — в stderr с
ненулевым кодом возврата). Несколько фоновых агентов могут безопасно писать
параллельно (WAL + `busy_timeout` + транзакция на операцию).

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

```bash
git clone https://github.com/mdemyanov/ktalk-mcp.git
cd ktalk-mcp
uv sync

# Запуск тестов
uv run pytest -v

# Линтинг
uv run ruff check .

# Локальный запуск сервера (session token или KTALK_PERSONAL_API_KEY — см. «Авторизация»)
KTALK_SESSION_TOKEN=... KTALK_BASE_URL=... uv run ktalk-mcp
```

## Лицензия

MIT
