ktalk-mcp
# ktalk-mcp
MCP-сервер для Контур.Толк: создание комнат, планирование встреч, список и скачивание
записей, чтение чата встреч, серверный транскрипт записи — напрямую через HTTP API Толка.
Авторизация — два ключа, каждый в своём файле:
- **Персональный API-ключ** (`X-Auth-Token: talkusr.…`) живёт год. Им ходят все тулы, кроме
архива встреч и чата.
- **Session-токен** браузера (`Authorization: Session <token>`) живёт ~30 дней. Он нужен для
`list_meetings`, `get_meeting` и `get_chat_messages`, а без ключа — для всего. Обновляется
вручную через DevTools, букмарклетом из браузера или через `chromedb` CLI.
## Инструменты
Инструменты разложены по сущностям: **Комната** → **Встреча** → **Запись** → **Чат**.
Связка между встречей и записью — `conference_key` (одинаково в выхлопе и в параметрах).
| Инструмент | Назначение |
|---|---|
| `create_room` | Создать/пересоздать комнату |
| `create_meeting` | Запланировать встречу в календаре |
| `list_meetings` | Архив прошедших встреч с пагинацией, поиском и окном по датам |
| `get_meeting` | Детали встречи с полным составом участников |
| `list_recordings` | Список записей с пагинацией и поиском |
| `get_recording` | Детали записи со ссылкой на скачивание |
| `download_recording` | Скачать видеофайл записи |
| `get_chat_messages` | Чат встречи |
| `get_transcript` | Серверный транскрипт записи (`[мм:сс] Имя: текст`) |
| `auth_status` | Состояние ключа и Session-токена, сроки, пути к файлам |
## Модель данных и связи
```mermaid
flowchart TB
room["Комната<br/>/api/rooms/{slug}"]
meeting["Конференция<br/>/api/conferencesHistory/{conference_key}"]
rec["Запись<br/>/api/recordings/{recording_id}<br/>видео + участники"]
chat["Чат — артефакт встречи<br/>/api/conferencesHistory/{key}/chat"]
room -->|"одна комната → N встреч"| meeting
meeting -->|"recording_ids"| rec
rec -.->|"conference_key"| meeting
meeting -->|"conference_key"| chat
rec -.->|"conference_key"| chat
classDef core fill:#1168bd,stroke:#0b4884,color:#ffffff
classDef art fill:#6b4fbb,stroke:#4a3785,color:#ffffff
class room,meeting core
class rec,chat art
```
## Установка
### Переменные окружения
| Переменная | Обязательна | По умолчанию | Назначение |
|---|---|---|---|
| `KTALK_SPACE_URL` | **да** | — | Адрес пространства Толка, например `https://<your-space>.ktalk.ru` |
| `KTALK_TOKEN_FILE` | нет | `~/.config/ktalk-mcp/token` | Файл Session-токена (0600); папка и файл создаются при старте |
| `KTALK_PAT_FILE` | нет | `~/.config/ktalk-mcp/pat` | Файл персонального API-ключа (0600); нет файла — всё идёт по Session |
| `KTALK_RECEIVER_PORT` | нет | `8765` | Локальный порт, на который букмарклет отправляет токен |
| `KTALK_VERIFY_SSL` | нет | `false` | Проверка TLS-сертификата; `true` — требовать валидный сертификат |
| `KTALK_LOG_LEVEL` | нет | `INFO` | Уровень логов (в stderr) |
### Claude Desktop / Claude Code — uvx (без установки)
```json
{
"mcpServers": {
"ktalk": {
"command": "uvx",
"args": ["--from", "git+https://github.com/mainpart/ktalk-mcp", "ktalk-mcp"],
"env": { "KTALK_SPACE_URL": "https://<your-space>.ktalk.ru" }
}
}
}
```
## Персональный API-ключ
Ключ создаётся в Толке: *Настройки → API ключи* (или `POST /api/user/personal-access-tokens`
под Session). Значение `talkusr.…` показывается один раз. Положите его в файл ключа: строкой
или JSON со сроком, тогда `auth_status` покажет, сколько дней осталось:
```bash
(umask 077; echo '{"token":"talkusr.…","expiresAt":"2027-10-19T00:00:00Z"}' > ~/.config/ktalk-mcp/pat)
```
Что открывает ключ (проверено 20.10.2026):
| Эндпоинт | Ключ | Session |
|---|---|---|
| `/api/recordings*`, `/recording-blob/*` | да | да |
| `/api/rooms`, `/api/context` | да | да |
| `/api/mcp` (официальный MCP Толка) | да, один POST без сессии | да, через `initialize` |
| `/api/conferencesHistory*` (архив встреч, чат) | **нет, 403** | да |
| `/api/user/personal-access-tokens` | нет, 403 | да |
Сервер сам выбирает ключ: для `/conferencesHistory*` берёт Session, для остального ключ,
если он есть. Если Session протух, `list_meetings`, `get_meeting` и `get_chat_messages`
отвечают `not_authenticated` с пояснением, что ключ туда не пускает. Остальные тулы
продолжают работать.
Если ключ отозвали раньше срока, сервер повторяет запрос по Session. Мёртвый ключ Толк
не всегда отмечает кодом 401: `/recordings/{id}`, `/recording-blob` и `/rooms/{name}`
отвечают на него 403, а официальный MCP отвечает 200 с текстом ошибки. Поэтому при 403
или ошибке MCP сервер отдельно проверяет ключ или токен запросом `GET /api/recordings?top=1`
и возвращает `not_authenticated` только в том случае, если тот действительно не работает.
`auth_status` по сети не ходит и показывает, что работает по срокам из файлов:
`meetings_and_chat_ok` — архив встреч и чат (нужен Session), `other_tools_ok` — всё
остальное. Ключ, отозванный раньше `expiresAt`, там по-прежнему виден как рабочий.
Отозвать ключ можно там же, в *Настройки → API ключи*, или через
`DELETE /api/user/personal-access-tokens/{key}`.
## Транскрипт записи
`get_transcript(recording_id)` вызывает `talk_get_transcript` официального MCP Толка
(`{space}/api/mcp`). Подряд идущие реплики одного спикера склеиваются, таймкод берётся
от первой. Ответ: `{recording_id, title, conference_key, status, text}`.
`status` передаётся от Толка без изменений: `Ready`, `NotReady` (ещё обрабатывается) или
`Unavailable`. `Unavailable` Толк отдаёт и на несуществующую запись, и на доступную запись
без транскрипта. Различить их можно по `title`: у несуществующей он `null`. Если статус не
`Ready`, `text` пустой, а `note` подсказывает запасной путь: `download_recording` и локальная
расшифровка.
## Получение Session-токена
Каждый способ кладёт Session-токен в файл токена. Путь по умолчанию —
`~/.config/ktalk-mcp/token`.
### Способ 1 — консоль DevTools
На вкладке, где вы залогинены в `https://<your-space>.ktalk.ru`, откройте DevTools →
Console и выполните:
```js
copy(JSON.parse(localStorage.session).data.token)
```
Вставьте скопированную строку в файл токена:
```bash
pbpaste > ~/.config/ktalk-mcp/token && chmod 600 ~/.config/ktalk-mcp/token
```
### Способ 2 — букмарклет (один клик, сохраняет сам)
Букмарклет читает токен (по возможности перевыпускает его на полный срок), отправляет
на localhost:8765 (слушающий сервер стартует вместе с mcp). Добавьте это как закладку:
```
javascript:(async()=>{try{const s=JSON.parse(localStorage.session||"{}"),d=s.data||{};let t=d.token,e=d.expiresAt||null;try{if(d.idToken){const r=await fetch("/api/authorize/session",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({idToken:d.idToken})});if(r.ok){const j=await r.json();if(j&&j.token){t=j.token;e=j.expiresAt||e}}}}catch(_){}if(!t){alert("Толк: токен не найден в localStorage.session.data.token — залогинься и повтори.");return}const p=await fetch("http://127.0.0.1:8765/ktalk-token",{method:"POST",headers:{"Content-Type":"text/plain"},body:JSON.stringify({token:t,expiresAt:e})});const o=await p.json().catch(()=>({}));if(p.ok&&o.ok){alert("Токен Толка сохранён"+(o.expiresAt?", годен до "+o.expiresAt:"")+"\n"+(o.path||""))}else{alert("Не удалось сохранить токен: "+(o.message||p.status))}}catch(x){alert("Ошибка букмарклета: "+x)}})();
```
**Как добавить в Chrome:** показать панель закладок (`⌘⇧B`) → правый клик по ней →
*Добавить страницу…* → вставить строку выше в поле *URL*. Chrome иногда вырезает
`javascript:` при первой вставке — тогда откройте *Изменить* у закладки и вставьте ещё
раз, со второго раза он сохраняется.
**Как использовать:** находясь на вкладке своего пространства Толка (залогинены),
нажмите закладку. Появится `alert` вида *«Токен Толка сохранён, годен до …»*.
Инструменты начинают работать без перезапуска MCP-сервера.
> Порт в букмарклете по умолчанию — `8765`. Если задали `KTALK_RECEIVER_PORT`, поправьте
> порт в букмарклете под него.
> Приёмник принимает POST только со страницы, чей origin совпадает с `KTALK_SPACE_URL`
> (иначе `403 forbidden_origin`): порт слушается всё время жизни процесса, и без этой
> проверки токен-файл могла бы перезаписать любая открытая вкладка. Если букмарклет
> отвечает `403`, сверьте `KTALK_SPACE_URL` с адресом вкладки.
### Способ 3 — chromedb (из файлов профиля Chrome, без запуска браузера)
[chromedb](https://github.com/noperator/chromedb) читает localStorage Chromium прямо с
диска — удобно, когда не хочется открывать DevTools. `localStorage` не зашифрован, пароль
или связка ключей не нужны; работает и при запущенном браузере.
Установка (нужен Go):
```bash
go install -v github.com/noperator/chromedb/cmd/chromedb@latest
```
Укажите каталог профиля Chrome (точный путь — в `chrome://version`, строка «Profile Path»):
```bash
# macOS: ~/Library/Application Support/Google/Chrome/Default
# Windows: %LOCALAPPDATA%\Google\Chrome\User Data\Default
# Linux: ~/.config/google-chrome/Default
PROFILE="$HOME/Library/Application Support/Google/Chrome/Default"
```
Вывести токен строкой:
```bash
chromedb -ls -p "$PROFILE" | jq -r 'select(.storage_key=="https://<your-space>.ktalk.ru" and .script_key=="session") | .value.data.token'
```TDQS
Scored across 9 tools
Each tool targets a distinct resource and action: room creation, meeting creation, meeting listing/detail, recording listing/detail/download, chat retrieval, and auth status. There is no overlap or ambiguity between any two tools.
The naming pattern is predominantly verb_noun (create_room, create_meeting, list_meetings, get_meeting, list_recordings, get_recording, download_recording, get_chat_messages). The only outlier is auth_status, which uses a noun_noun form, slightly breaking the otherwise consistent convention.
With 9 tools, the server is well-scoped for its purpose of managing meetings, recordings, rooms, and chat. The count is neither too sparse nor overwhelming, and each tool contributes a distinct capability.
The tool set provides solid coverage for creating rooms and meetings, browsing meetings and recordings, downloading recordings, and fetching chat messages. However, there are no update/delete operations for rooms or meetings, and no list/get for rooms, which are minor gaps for full lifecycle management.