Skip to main content
Glama
ssm82

syntx-ai-mcp

by ssm82
README.md
<div align="center">

# syntx-ai-mcp

**MCP-сервер и TypeScript SDK для AI-платформы [syntx.ai](https://syntx.ai)**

Превратите любой MCP-совместимый ассистент (Claude Desktop, Cursor, VS Code, Cline) в полнофункционального клиента syntx.ai: чаты, генерация изображений, каталог моделей, управление аккаунтом — всё через единый протокол Model Context Protocol.

[![MCP](https://img.shields.io/badge/MCP-1.0+-6BA7E1)](https://modelcontextprotocol.io)
[![Node](https://img.shields.io/badge/node-%3E%3D18-339933?logo=node.js&logoColor=white)](https://nodejs.org)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.4-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

</div>

---

## Содержание

- [Обзор](#обзор)
- [Возможности](#возможности)
- [Требования](#требования)
- [Быстрый старт](#быстрый-старт)
- [Подключение к клиентам](#подключение-к-клиентам)
  - [Claude Desktop](#claude-desktop)
  - [Cursor](#cursor)
  - [VS Code (Copilot / Insiders)](#vs-code-copilot--insiders)
  - [Cline](#cline)
  - [Continue / Windsurf](#continue--windsurf)
  - [HTTP / SSE (любой клиент)](#http--sse-любой-клиент)
- [Переменные окружения](#переменные-окружения)
- [Транспорты](#транспорты)
- [Инструменты (Tools)](#инструменты-tools)
  - [Идентификация и токен](#идентификация-и-токен)
  - [Каталог AI](#каталог-ai)
  - [Чаты и сообщения](#чаты-и-сообщения)
  - [Генерация медиа](#генерация-медиа)
  - [Аудио](#аудио)
  - [Аккаунт пользователя](#аккаунт-пользователя)
  - [Файлы](#файлы)
- [Безопасность](#безопасность)
- [Troubleshooting](#troubleshooting)
- [Получение токена](#получение-токена)
- [Программное использование (SDK)](#программное-использование-sdk)
- [Примеры](#примеры)
- [Разработка](#разработка)
- [Как это работает](#как-это-работает)
- [Дорожная карта](#дорожная-карта)
- [Лицензия](#лицензия)

---

## Обзор

**syntx-ai-mcp** — это сервер [Model Context Protocol](https://modelcontextprotocol.io), который открывает возможности платформы syntx.ai AI-ассистентам по единому стандарту. Вместо интеграции проприетарного API в каждый инструмент, вы один раз запускаете MCP-сервер — и любой MCP-клиент получает доступ к:

- 💬 **Чатам и моделям** — создание сессий, отправка промптов, ожидание ответа (включая one-shot `ask`).
- 🎨 **Генерации медиа** — изображения (Sora, Flux и другие design-сервисы), аудио и видео.
- 📚 **Каталогу** — AI-сервисы, модели с ограничениями.
- 👤 **Аккаунту** — профиль, баланс токенов.
- 📁 **Файлам** — список и удаление загруженных файлов.

Пакет распространяется как **два-в-одном**: готовый MCP-сервер (`syntx-mcp` CLI) и полноценный типизированный SDK (`SyntxClient`) для прямого программного использования.

## Возможности

| Группа | Что входит |
|---|---|
| 🛠️ **49 инструментов** | Идентификация, чаты, генерация медиа (изображения, аудио, видео), транскрипция, каталог, аккаунт, файлы, проекты, тарифы/цены, discovery (тренды/события), улучшение промптов |
| 🔌 **2 транспорта** | stdio (по умолчанию) и stateless HTTP/SSE |
| 🔐 **Токен в рантайме** | Задавайте токен без перезапуска (`set-token`); проверяйте статус через `whoami` |
| 🧱 **Типобезопасность** | Полная типизация TypeScript, JSON Schema для каждого инструмента |
| 🌐 **Dual-формат** | Сборка CJS + ESM + `.d.ts` |

## Требования

- **Node.js ≥ 18** (использует встроенный `fetch`)
- Учётная запись и **bearer-токен** [syntx.ai](https://syntx.ai)
- MCP-совместимый клиент (Claude Desktop, Cursor, VS Code Insiders, Cline, …)

---

## Быстрый старт

```bash
# 1. Установить пакет
npm install syntx-ai-mcp

# 2. Собрать (если клонировали репозиторий)
npm install && npm run build

# 3. Запустить MCP-сервер (stdio — стандарт для локальных клиентов)
SYNTX_TOKEN="ваш-токен" npx syntx-ai-mcp
```

Готово — теперь подключите сервер к вашему ассистенту (см. ниже).

> **Токен можно не задавать заранее.** Запустите сервер без `SYNTX_TOKEN` и вызовите инструмент `set-token` прямо из чата — токен применится в рантайме.

---

## Подключение к клиентам

### Claude Desktop

Отредактируйте конфиг Claude Desktop:

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

```json
{
  "mcpServers": {
    "syntx-ai": {
      "command": "npx",
      "args": ["-y", "syntx-ai-mcp"],
      "env": {
        "SYNTX_TOKEN": "ВАШ_ТОКЕН"
      }
    }
  }
}
```

Если пакет собран локально — используйте прямой путь:

```json
{
  "mcpServers": {
    "syntx-ai": {
      "command": "node",
      "args": ["/путь/к/syntx-ai-mcp/dist/bin/cli.js"],
      "env": { "SYNTX_TOKEN": "ВАШ_ТОКЕН" }
    }
  }
}
```

После сохранения перезапустите Claude Desktop. В чате появятся инструменты `ask`, `list-models` и др. — Claude будет вызывать их автоматически.

### Cursor

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

```json
{
  "mcpServers": {
    "syntx-ai": {
      "command": "npx",
      "args": ["-y", "syntx-ai-mcp"],
      "env": { "SYNTX_TOKEN": "ВАШ_ТОКЕН" }
    }
  }
}
```

В Cursor: **Settings → Cursor Settings → Features → MCP → Add new MCP Server**.

### VS Code (Copilot / Insiders)

Файл `.vscode/mcp.json` в workspace:

```json
{
  "servers": {
    "syntx-ai": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "syntx-ai-mcp"],
      "env": { "SYNTX_TOKEN": "ВАШ_ТОКЕН" }
    }
  }
}
```

Откройте Command Palette → **MCP: List Servers**, чтобы убедиться, что `syntx-ai` активен.

### Cline

Файл `cline_mcp_settings.json` (через интерфейс Cline → MCP Servers):

```json
{
  "mcpServers": {
    "syntx-ai": {
      "command": "npx",
      "args": ["-y", "syntx-ai-mcp"],
      "env": { "SYNTX_TOKEN": "ВАШ_ТОКЕН" },
      "disabled": false,
      "autoApprove": []
    }
  }
}
```

### Continue / Windsurf

Используйте стандартный stdio-блок `command`/`args`/`env` (формат идентичен Claude Desktop). Для Windsurf: **Settings → MCP Servers → Add Server**.

### HTTP / SSE (любой клиент)

Запустите сервер в HTTP-режиме и подключите клиента по URL:

```bash
SYNTX_TOKEN="ВАШ_ТОКЕН" npx syntx-ai-mcp --transport http --http-port 8080
# MCP endpoint: http://127.0.0.1:8080/mcp
# Health check:  http://127.0.0.1:8080/health
```

```json
{
  "mcpServers": {
    "syntx-ai": { "url": "http://127.0.0.1:8080/mcp" }
  }
}
```

---

## Переменные окружения

| Переменная | Тип | По умолчанию | Описание |
|---|---|---|---|
| `SYNTX_TOKEN` | string | — | Bearer-токен syntx.ai. Обязателен для большинства операций (можно задать через `set-token`). |
| `SYNTX_BASE_URL` | string | `https://api.syntx.ai` | Базовый URL API. |
| `SYNTX_TIMEOUT` | number | `30000` | Таймаут HTTP-запроса, мс. |
| `SYNTX_LANG` | string | `en` | Локаль API (например, язык ответов тарифных планов). Не влияет на язык генерации моделей. |
| `SYNTX_DEFAULT_AI` | string | `chatgpt` | AI-сервис по умолчанию для `send-message`/`ask`. |
| `SYNTX_DEFAULT_MODEL` | string | — | Модель по умолчанию. |
| `SYNTX_POLL_INTERVAL` | number | `5000` | Интервал polling ответа, мс. |
| `SYNTX_POLL_TIMEOUT` | number | `600000` | Максимальное ожидание ответа, мс. |
| `SYNTX_STREAM_MODE` | `auto` \| `stream` \| `poll` \| `off` | `auto` | Стратегия стриминга для `ask` / `stream-message`. |
| `SYNTX_LLM_SSE_BASE_URL` | string | `https://sse.syntx.ai` | Базовый URL для SSE-стрима, используемого text-flow `llm/*`. |
| `SYNTX_LLM_MODELS_CACHE_MS` | number | `60000` | TTL in-memory кэша для `syntx.llm.listModels()`. |
| `MCP_TRANSPORT` | `stdio` \| `http` | `stdio` | Транспорт MCP-сервера. |
| `MCP_HTTP_PORT` | number | `3000` | Порт HTTP-транспорта. |
| `MCP_HTTP_HOSTNAME` | string | `127.0.0.1` | Адрес привязки HTTP-транспорта (loopback по умолчанию). |
| `MCP_HTTP_TOKEN` | string | — | Bearer-токен для самого MCP-сервера (HTTP-транспорт). Если задан — запросы без совпадающего заголовка `Authorization: Bearer` отклоняются (401, timing-safe сравнение). Если не задан — loopback-only + предупреждение. |

Альтернативно — флаги CLI: `--token`, `--base-url`, `--transport`, `--http-port`. Флаги приоритетнее env.

---

## Транспорты

**syntx-ai-mcp** поддерживает два транспорта Model Context Protocol:

### stdio (по умолчанию)

Клиент запускает сервер как дочерний процесс и общается через стандартные потоки. Рекомендуется для **локальных** ассистентов (Claude Desktop, Cursor, VS Code, Cline). Минимальные задержки, нулевая сетевая конфигурация.

```bash
npx syntx-ai-mcp                          # stdio
npx syntx-ai-mcp --transport stdio        # явно
```

### HTTP + SSE

Stateless Streamable HTTP: на каждый запрос создаётся свежий transport + server (канонический паттерн MCP SDK). Подходит для **удалённых**, облачных и веб-клиентов. Поддерживает health-check `/health`.

```bash
npx syntx-ai-mcp --transport http --http-port 8080
# MCP endpoint:  http://127.0.0.1:8080/mcp
# Health check:  http://127.0.0.1:8080/health
```

**Безопасность HTTP-транспорта:**
- **Host/Origin allow-list** включён всегда (защита от DNS-rebinding): запросы с `Host`/`Origin`, не входящим в `{127.0.0.1, localhost, ::1, <bind-host>}`, отклоняются (403).
- **Bearer-аутентификация** (`MCP_HTTP_TOKEN`): если задан, каждый запрос `/mcp` должен нести заголовок `Authorization: Bearer <MCP_HTTP_TOKEN>` (timing-safe сравнение, схема регистронезависима). Иначе — 401.
- `OPTIONS` (CORS preflight) отвечает `200` без проверки токена; wildcard `Access-Control-Allow-Origin` не выдаётся.
- Если `MCP_HTTP_TOKEN` не задан — сервер работает только на loopback и печатает предупреждение. **Не выставляйте HTTP-транспорт в публичные сети без `MCP_HTTP_TOKEN` и файрвола.**

```bash
MCP_HTTP_TOKEN="your-mcp-secret" npx syntx-ai-mcp --transport http --http-port 8080
```

```json
{
  "mcpServers": {
    "syntx-ai": {
      "url": "http://127.0.0.1:8080/mcp",
      "headers": { "Authorization": "Bearer your-mcp-secret" }
    }
  }
}
```

---

## Инструменты (Tools)

Все 58 инструментов принимают JSON-аргументы и возвращают структурированный результат. Текстовые ответы — это JSON-снимки данных API; ошибки возвращаются с `isError: true` (без обрыва канала).

Аргументы проверяются по JSON-схеме инструмента **до** вызова хендлера (ajv, единая точка входа `call-tool`): `enum`, `required` и `additionalProperties: false` реально применяются, а нарушение возвращается как `isError: true` с указанием пути аргумента и требуемого значения. Типы не приводятся принудительно, значения по умолчанию из схемы не подставляются.

### Идентификация и токен

| Инструмент | Описание | Параметры |
|---|---|---|
| `whoami` | Идентификационная *проверка*: `{ authenticated, user }`. **Никогда не возвращает ошибку** при отсутствии/невалидности (401/403) токена — сообщает `authenticated: false`. Реальные сбои (сеть, 5xx) всё же дают `isError`. | — |
| `get-profile` | Полный профиль пользователя; при отсутствии токена возвращает понятную MCP-ошибку. | — |
| `list-auth-identities` | Связанные внешние идентичности (Telegram, email, …) с `provider`, `email`, `username`, `linked_at`. Только документированные поля; всё, что upstream добавит сверх этого, отбрасывается на границе SDK. | — |
| `get-chatwoot-unread` | Состояние непрочитанных сообщений в support-канале: `{contact_found, has_unread}`. HMAC `chatwoot_hmac`, которым гейтится вызов, не выходит наружу. | — |
| `set-token` | Установить/заменить токен в рантайме (только в памяти — не переживает рестарт). | `token`* |

> `whoami` и `get-profile` различаются **семантикой ошибок**, а не составом полей (оба берут данные из одного `user.me()`). Используйте `whoami` для проверки статуса аутентификации без риска получить ошибку, `get-profile` — когда нужен полный профиль и готов обработать ошибку при отсутствии токена.

> `*` — обязательный параметр.

### Каталог AI

| Инструмент | Описание | Параметры |
|---|---|---|
| `list-ai-services` | Доступные AI-сервисы (ChatGPT, Midjourney, Sora…) | — |
| `list-models` | Модели с ограничениями и поддерживаемыми форматами | `scope?`, `ai_name?`, `active_only?`, `search?`, `source?` |

Параметры `list-models` (все опциональны, комбинируются через AND):

- `scope` — категория возможностей: `text` \| `image` \| `video` \| `audio` \| `upscale`. Категория выводится из живого каталога провайдеров (`GET /api/v1/ai`); если провайдер неизвестен, модель попадает только в вызовы без фильтра `scope`.
- `ai_name` — точное имя провайдера syntx.ai, например `"chatgpt"`, `"claude"`, `"midjourney"`.
- `active_only` — `true` (по умолчанию) скрывает неактивные модели. Передайте `false`, чтобы получить весь каталог.
- `search` — регистронезависимая подстрока по идентификатору модели и `label` (например, `"gpt-5"`, `"claude-opus-5"`).
- `source` — какой каталог читать: `"ai"` (**по умолчанию**) — платформенный каталог: `GET /api/v1/ai/models` (все медиа-скоупы), объединённый с `GET /api/v1/llm/models`; `"llm"` — только текстовый каталог (тот вид, который видит пикер SPA); `"both"` — явная запись того же объединения, что и `"ai"`. Каталоги upstream **не синхронизированы**: `gpt-6.1-sol` / `gpt-6.1-terra` есть только в `llm`, а `deepseek-r1`, `gemini-3.7-flash`, `grok-4.6` включены для текста, но помечены неактивными в `ai`. Поэтому платформенный каталог (и `"ai"`, и `"both"`) содержит обе половины: в общем каталоге `gpt-6.1-sol` присутствует, а из текстовых видны и те модели, что помечены неактивными в `ai`.

Поле `value` — всегда идентификатор модели (в текстовом каталоге это `id`). Для строк из LLM-каталога дополнительно приходит объект `llm` с `context_window`, `default_max_tokens`, `capabilities`, `supported_tools`, `min_subscription`.

Пример:

```json
{
  "name": "list-models",
  "arguments": {
    "source": "ai",
    "scope": "text",
    "ai_name": "chatgpt",
    "search": "gpt-5"
  }
}
```

### Чаты и сообщения

| Инструмент | Описание | Параметры |
|---|---|---|
| `list-chats` | Список чатов с фильтрами | `scope?`, `search?`, `direction?`, `page_size?` |
| `create-chat` | Создать чат (обязателен `title`) | `title`*, `scope?`, `model?` |
| `rename-chat` | Переименовать чат (`PUT /api/v1/chats/{chat_id}`) | `chat_id`*, `title`* |
| `chat-exists` | Pre-flight проверка: существует ли чат (`GET /api/v1/chats/{chat_id}`; 404 → `false`) | `chat_id`* |
| `get-messages` | История сообщений чата | `chat_id`*, `page_size?`, `direction?` |
| `get-inprogress` | Активные генерации чата (пустой массив — ничего не генерируется) | `chat_id`* |
| `get-favorite-messages` | Избранные (звёздные) сообщения чата — `get-messages` их не включает | `chat_id`*, `page_size?`, `direction?` |
| `send-message` | Отправить промпт с опциональными вложениями, вернуть ack (ответ — асинхронно) | `chat_id`*, `prompt`*, `ai_name?`, `model_type?`, `attachments?` |
| `wait-for-response` | Дождаться завершения генерации и вернуть текст + media-объекты | `chat_id`*, `timeout?`, `poll_interval?` |
| `ask` ⭐ | One-shot: создать чат → отправить → дождаться ответа | `prompt`*, `title?`, `ai_name?`, `model_type?`, `scope?`, `timeout?`, `poll_interval?`, `mode?` |
| `stream-message` 🌊 | One-shot со стримингом ответа по SSE (`sse.syntx.ai`) + `notifications/progress` | `prompt`*, `scope?`, `model?`, `ai_name?`, `model_type?`, `timeout?`, `mode?` |
| `cancel-message` | Отменить in-flight генерацию сообщения | `chat_id`*, `message_id`* |
| `delete-message` | Удалить отдельное сообщение (`DELETE /api/v1/chats/messages/{message_id}` — без chat id в пути); деструктивно | `message_id`* |
| `delete-chat` | Удалить чат без возможности восстановления (`DELETE /api/v1/chats/{chat_id}`); деструктивно | `chat_id`* |
| `toggle-chat-favorite` | Переключить флаг «избранное» чата: каждый вызов инвертирует состояние; проверить через `list-chats` | `chat_id`* |
| `toggle-message-favorite` | Переключить флаг «избранное» сообщения: каждый вызов инвертирует состояние; читать через `get-favorite-messages` | `chat_id`*, `message_id`* |
| `get-llm-limits` | Текущие LLM-лимиты (окна 6h / 7d) + план подписки `plan` | — |
| `get-llm-reset-price` | Квота на сброс LLM-окон (`available`, `price_tokens`, `used_percent`, `reason`) | — |

> ⭐ **`ask`** — главный инструмент для stateless Q&A. Возвращает `chat_uuid` для последующих уточнений через `send-message` + `wait-for-response`.
>
> 🌊 **`stream-message`** для text-scope открывает SSE-соединение на `sse.syntx.ai` и доставляет токены по мере поступления; при сбое транспорта автоматически переключается на REST-поллинг. Прогресс отправляется через MCP-нотификации (`notifications/progress` + `notifications/message`); финальный результат содержит полный текст и метаданные (`chat_uuid`, `elapsed_ms`, `chunks`).

**`ask` vs `stream-message` vs низкоуровневый flow:**

| Подход | Инструменты | Когда использовать |
|---|---|---|
| Быстрый вопрос (блокирующий) | `ask` | Обычный пользовательский запрос; поддерживает `mode: auto\|stream\|poll\|off` |
| Стриминг ответа | `stream-message` | Длинные ответы, UX с прогрессом; режимы `auto\|stream\|poll` (**`off` не поддерживается**) |
| Полный контроль | `create-chat` → `send-message` → `wait-for-response` → `get-messages` | Многошаговый диалог, кастомная логика |

Стратегией управляет `SYNTX_STREAM_MODE`. **Важно:** значение `off` влияет только на `ask` (fire-and-forget: создать чат, отправить промпт, сразу вернуть `chat_uuid`). У `stream-message` нет режима `off`.

**Как `wait-for-response` определяет «готово»:** инструмент резолвится, когда **все** объекты `message_object[i].completed === true`. Это включает ответы, состоящие только из `image` / `video` / `audio` / `file` — раньше такие генерации зависали до таймаута, потому что проверка требовала непустой `object_text` на объекте `[0]`. URL медиа-объектов возвращаются в блоке `media` (JSON) между текстом и метаданными; `metadata` с сервера пробрасывается как есть (без парсинга). Серверного `cancel`-эндпоинта нет — клиентский `AbortSignal` останавливает только локальный цикл опроса.

**Пример вызова `ask`:**

```json
{
  "name": "ask",
  "arguments": {
    "prompt": "Объясни квантовую запутанность простыми словами",
    "ai_name": "chatgpt",
    "model_type": "gpt-5-mini-2025-08-07"
  }
}
```

> Идентификаторы моделей зависят от провайдера и могут меняться. Получите актуальный список через инструмент `list-models` (например, `list-models` с `scope: "text"` и `ai_name: "chatgpt"`).

**Пример вызова с явной моделью:**

```json
{ "name": "ask", "arguments": { "prompt": "Привет!", "ai_name": "chatgpt", "model_type": "gpt-5.5" } }
```

Явный `model_type` переопределяет умолчания `SYNTX_DEFAULT_AI` / `SYNTX_DEFAULT_MODEL`; актуальные идентификаторы моделей получайте через `list-models`.

### Генерация медиа

| Инструмент | Описание | Параметры |
|---|---|---|
| `generate-image` | Генерация изображений через design-сервис | `chat_uuid`*, `prompt`*, `ai_name?`, `n?`, `model_type?`, `resolution?`, `quality?`, `image_url?` |
| `generate-video` | Генерация видео (`wan_video`, `runway`, `kling`…) — долгая операция, ждите через `wait-for-response` | `chat_id`* (не `chat_uuid`!), `prompt`*, `ai_name?`, `model_type?`, `duration?`, `resolution?`, `aspect_ratio?`, `fps?`, `quality?`, `seed?`, `file_urls?`, `audio_url?` |

```json
{
  "name": "generate-image",
  "arguments": {
    "chat_uuid": "131c1065-644a-492f-a1ff-cdb6ba7d8560",
    "prompt": "Космический корабль в стиле киберпанк, неоновые огни",
    "resolution": "720x1280",
    "quality": "medium",
    "n": 1
  }
}
```

> Сначала создайте чат через `create-chat`, чтобы получить `chat_uuid`.
> Результат — JSON-метаданные генерации, которые возвращает design-сервис syntx.ai (состав полей зависит от сервиса; обычно содержит ссылки на сгенерированные изображения и метаданные запроса).

### Аудио

| Инструмент | Описание | Параметры |
|---|---|---|
| `transcribe` | Транскрипция аудио в текст (`POST /api/v1/audio/transcribe`). Возвращает `{ text }`. | `path` или `content_base64`*, `filename?`, `mime_type?` |
| `generate-audio` | TTS / voice-change / генерация музыки (`elevenlabs`, `suno-music`…) | `chat_uuid`*, `prompt`*, `ai_name?`, `voice_id?`, `model_type?`, `style_prompt?`, `duration?`, `sample_rate?`, `file_urls?` |

Один файл передаётся либо как `path` (путь на ФС сервера; **только stdio-транспорт**), либо как `content_base64` с обязательным `filename`.

> ⚠️ **Безопасность:** при HTTP-транспорте `path` **отклоняется** (произвольное чтение файлов сервера удалённым клиентом) — используйте `content_base64`. Лимит 50 МБ (на декодированный файл), форматы: mp3, wav, mpeg.

```json
{
  "name": "transcribe",
  "arguments": {
    "content_base64": "data:audio/mpeg;base64,//uQxAAAAA...",
    "filename": "meeting.mp3"
  }
}
```

### Аккаунт пользователя

| Инструмент | Описание |
|---|---|
| `get-profile` | Профиль (имя, email, аватар, auth-сервисы) |
| `get-balance` | Баланс токенов |
| `get-tokens-history` | История трат токенов (cursor-пагинация) |

### Файлы

| Инструмент | Описание | Параметры |
|---|---|---|
| `list-uploaded-files` | Список загруженных файлов | `scope?`, `page?`, `page_size?` |
| `list-generated-files` | Список сгенерированных файлов (image/video/audio) | `scopes?`, `page?`, `page_size?` |
| `upload-files` | Загрузить до 10 файлов (≤ 100 МБ каждый) | `files`*, `check_duplicates?` |
| `delete-file` | Удалить файл | `file_id`* |

Каждый элемент массива `files` в `upload-files` принимает **одно из двух**:

- `{ path }` — путь к файлу на машине, где запущен MCP-сервер (stdio/HTTP-сервер должен иметь доступ к ФС).
- `{ content_base64, filename }` — base64-payload (можно с префиксом `data:<mime>;base64,`). `filename` обязателен, `mime_type` опционален и подбирается по расширению.

Пример (смешанные источники):

```json
{
  "name": "upload-files",
  "arguments": {
    "files": [
      { "path": "C:\\Users\\me\\photo.jpg" },
      {
        "content_base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
        "filename": "pixel.png"
      }
    ],
    "check_duplicates": true
  }
}
```

Чтобы прикрепить загруженный файл к сообщению, передайте поля из ответа `upload-files` в `attachments`:

```json
{
  "name": "send-message",
  "arguments": {
    "chat_id": "<chat-uuid>",
    "prompt": "Опиши изображение",
    "model_type": "<model-id>",
    "attachments": [
      {
        "url": "https://r2.syntx.ai/.../pixel.png",
        "filename": "pixel.png",
        "mime_type": "image/png"
      }
    ]
  }
}
```

`send-message` преобразует MIME-тип в категорию syntx.ai (`image`, `video`, `audio` или `file`). Категорию можно задать явно полем `type`.

### Проекты (папки)

| Инструмент | Описание | Параметры |
|---|---|---|
| `list-projects` | Список проектов для scope (`GET /api/v1/folders/{scope}/list`). Возвращает конверт `{folders: Folder[], pagination: Pagination}` — не голый массив. | `scope?` (`text` по умолчанию; `text` / `image` / `video` / `audio`) |
| `create-project` | Создать проект (a.k.a. папку) на syntx.ai; опционально сразу добавить чаты. | `title`*, `scope?` (`text` по умолчанию), `color?` (`#9C9C9C` по умолчанию), `chat_uuids?` |
| `add-chats-to-project` | Добавить один или несколько существующих чатов в проект (`POST /api/v1/folders/{folder_uuid}/add`). | `folder_uuid`*, `chat_uuids`* (≥ 1, `uniqueItems`) |
| `remove-chats-from-project` | Убрать чаты из проекта (`POST /api/v1/folders/{folder_uuid}/remove`, bare-массив UUID). | `folder_uuid`*, `chat_uuids`* (≥ 1, `uniqueItems`) |
| `update-project` | Изменить название и/или цвет проекта (`PATCH /api/v1/folders/{folder_uuid}/change`); отправляются только указанные поля. | `folder_uuid`*, `title?`, `color?` (хотя бы один) |
| `reorder-project` | Изменить порядок проекта (`PATCH /api/v1/folders/{folder_uuid}/move`); `after_uuid` опущен или `null` — переместить наверх. | `folder_uuid`*, `after_uuid?` |
| `delete-project` | Удалить проект без возможности восстановления (`DELETE /api/v1/folders/{folder_uuid}/delete`). | `folder_uuid`* |

> Серверная терминология — `folders`. В продукте это «проекты», в SDK — `syntx.folders.create` / `syntx.folders.addChats`.

Пример:

```json
{
  "name": "create-project",
  "arguments": {
    "title": "Refactor plan",
    "scope": "text",
    "color": "#FFAA00",
    "chat_uuids": ["47c2c3c5-f987-451e-9459-1ed4aaf45395"]
  }
}
```

```json
{
  "name": "add-chats-to-project",
  "arguments": {
    "folder_uuid": "475d21a2-221e-4f4e-83bf-16066ba33c4f",
    "chat_uuids": ["1cc76ce8-444b-4358-9ff5-dc77c01fb4fb"]
  }
}
```

### Агенты (AI agents)

Новая upstream-поверхность `agents/*` (prod-SPA bundle `1793202992`). Список агентов
смешивает собственные агенты аккаунта и платформенные (`editable: false`).

| Инструмент | Описание | Параметры |
|---|---|---|
| `list-agents` | Список агентов + квота (`{agents, agent_limit, agent_count, can_create_more}`) | — |
| `get-agent` | Полная карточка агента (`system_prompt`, `style_prompt`, `model_name`, `enabled_tools`, `default_models`, `files[]`) | `agent_id`* (UUID) |
| `create-agent` | Создать агента (`multipart/form-data`) | `name`*, `system_prompt`*, `model_name`*, `description?`, `style_prompt?`, `use_chat_history?`, `enabled_tools?`, `default_models?` |
| `update-agent` | Изменить агента (`PUT /api/v1/agents/update/{id}`) | `agent_id`* (числовой `id`), `name?`, `description?`, `system_prompt?`, `style_prompt?`, `model_name?`, `use_chat_history?`, `enabled_tools?`, `default_models?` |
| `delete-agent` | Удалить агента без возможности восстановления | `agent_id`* (UUID) |
| `list-agent-files` | Файлы, прикреплённые к агенту | `agent_id`* (UUID) |
| `add-agent-files` | Прикрепить файлы к агенту (`multipart/form-data`, поле `files`) | `agent_id`* (UUID), `files`* (≤ 10) |
| `delete-agent-files` | Открепить файлы от агента (`{file_ids: [...]}`) | `agent_id`* (UUID), `file_ids`* (≥ 1) |

> `agent_id` — это UUID (`agent_id` в ответе `list-agents`), а `update-agent` ждёт
> **числовой** `id` из `get-agent`. У `add-agent-files` тот же LFI-риск, что и у
> `upload-files`: `path` читает файл на машине сервера и запрещён на HTTP-транспорте.
> Чат с агентом идёт по WebSocket (`agents/chat/{agent_id}`) — MCP-сервер его не
> реализует, используйте SSE-текст-флоу.

### Тарифы и цены

| Инструмент | Описание | Параметры |
|---|---|---|
| `list-tariffs` | Тарифы (price list) для `monthly` / `annual` | `period?` |
| `list-token-pricing` | Прайс per-model (`{items: [{id, cost}]}`) | — |

### Discovery и события

| Инструмент | Описание | Параметры |
|---|---|---|
| `list-trend-categories` | Категории домашней ленты (`{categories: [{id, slug, title}]}`) | — |
| `list-trend-tags` | Теги домашней ленты (часто пусто) | — |
| `list-events` | Лента событий (промо, milestones) | `page?`, `page_size?` |

### Улучшение промптов

| Инструмент | Описание | Параметры |
|---|---|---|
| `get-prompt-enhancer-cost` | Стоимость улучшения промпта для данной модели | `model_type`* |
| `start-prompt-enhancement` | Запустить задачу улучшения (captcha-gated) | `prompt`*, `model_type`* |
| `get-prompt-enhancement-task` | Прочитать результат задачи | `task_id`* |

> `start-prompt-enhancement` 403-ит без `SYNTX_CAPTCHA_TOKEN` (или настроенного `captchaProvider`): endpoint `prompt-enhancer/enhance/start` попадает под rule `^prompt-enhancer\/(enhance|generate)(\/start)?$` с action `generate`.

---

## Безопасность

- **Токен syntx.ai (`SYNTX_TOKEN` / `set-token` / Telegram-flow) хранится только в памяти** — не пишется на диск, не переживает рестарт процесса, не логируется сервером. При `set-token` токен проходит через JSON-RPC-канал (по сети при HTTP-транспорте) — учитывайте логи вашего MCP-клиента.
- **HTTP-транспорт** по умолчанию слушает только `127.0.0.1` и **не имеет аутентификации**, пока не задан `MCP_HTTP_TOKEN`. Защита от DNS-rebinding обеспечивается Host/Origin allow-list (всегда включён). Для запуска вне loopback **обязательно** задайте `MCP_HTTP_TOKEN` и оградите порт файрволом/реверс-прокси.
- **Stateless HTTP = single-user loopback.** `set-token` меняет токен для всего процесса, поэтому HTTP-транспорт не предназначен для многопользовательского использования — один клиент установит токен, общий для всех.
- **`transcribe` с `path`** разрешён только при stdio-транспорте; при HTTP отклоняется (защита от произвольного чтения файлов сервера — LFI).
- **Аргументы инструментов валидируются по объявленной JSON-схеме** (ajv). Неверный `enum`, отсутствующий обязательный параметр или лишний аргумент отклоняются до вызова хендлера — хендлер никогда не получает «мусор» и не может молча подменить значение по умолчанию.
- Не передавайте `MCP_HTTP_TOKEN` в query-параметрах URL — только в заголовке `Authorization`.

---

## Troubleshooting

| Симптом | Вероятная причина | Решение |
|---|---|---|
| MCP-клиент не видит инструменты | Неверный путь к команде / Node.js < 18 | Проверьте путь, версию Node, логи клиента |
| `Authentication required or invalid` | Не задан/истёк токен | `set-token` или `SYNTX_TOKEN`; проверьте через `whoami` |
| HTTP `/mcp` возвращает 401 | Отсутствует/неверен `Authorization: Bearer` | Задайте `MCP_HTTP_TOKEN` и передавайте заголовок клиентом |
| HTTP `/mcp` возвращает 403 | `Host`/`Origin` не в allow-list | Используйте `127.0.0.1`/`localhost` либо `MCP_HTTP_HOSTNAME`, совпадающий с Host |
| Стриминг не приходит | `SYNTX_STREAM_MODE=off` или клиент не поддерживает progress | Проверьте `SYNTX_STREAM_MODE`; `off` отключает ожидание только у `ask` |
| Запрос долго висит | Малый `SYNTX_POLL_TIMEOUT` / большой `SYNTX_TIMEOUT` | Настройте таймауты под задачу |
| Модель не найдена | Неверный `model_type` | Вызовите `list-models` и передайте актуальный `model_type` в `ask` / `send-message` |
| `transcribe` отклоняет `path` | Используется HTTP-транспорт | Передайте аудио через `content_base64` |

---

## Получение токена

MCP-инструменты авторизации (Telegram / e-mail OTP) удалены при сокращении поверхности в v0.3.0. Токен устанавливается одним из способов:

- **Переменная окружения** `SYNTX_TOKEN` при запуске MCP-сервера.
- **Инструмент `set-token`** — задать/заменить JWT в рантайме (хранится только в памяти, не переживает рестарт).
- Проверка статуса — `whoami` (никогда не падает) или `get-profile`.

Сам JWT выдаёт платформа: войдите на syntx.ai и скопируйте токен из настроек профиля.

**Через SDK** доступны полноценные программные флоу авторизации — они не зависят от MCP-инструментов:

```ts
import { SyntxClient } from 'syntx-ai-mcp';

const syntx = new SyntxClient();

// Telegram device flow
const { token } = await syntx.auth.loginWithTelegram({
  onLink: (deepLink) => console.log('Откройте:', deepLink),
});

// Либо e-mail OTP
const { token: jwt } = await syntx.auth.loginWithEmail('user@example.com', {
  otpProvider: async () => askUserForOtp(),
});
```

> **Где живёт токен:** JWT хранится только в памяти процесса. После рестарта MCP-сервера задайте его заново (`SYNTX_TOKEN` или `set-token`). Не передавайте токены в query-параметрах — только в `Authorization: Bearer`. Полный список методов SDK: `syntx.auth.startAuth` / `pollAuthToken` / `sendEmailOtp` / `verifyEmailOtp` / `loginWithEmail` / `loginWithTelegram`.

---

## Программное использование (SDK)

Помимо MCP-сервера, пакет экспортирует типизированный SDK для прямого использования:

```ts
import { SyntxClient } from 'syntx-ai-mcp';

const syntx = new SyntxClient({ token: 'your-token' });

// Профиль и баланс
const me = await syntx.user.me();
const { balance } = await syntx.user.getBalance();

// Список моделей
const models = await syntx.ai.listModels();

// Создать чат и отправить сообщение через llm/* text-flow
const chat = await syntx.chats.create({ scope: 'text', title: 'Demo' });
await syntx.llm.generate({
  prompt: 'Привет!',
  aiName: 'chatgpt',
  modelType: 'your-model-id',
  chatUuid: chat.uuid,
});

// Дождаться ответа (SSE primary + REST polling fallback)
const completed = await syntx.llm.waitForResponse(chat.uuid, { timeout: 60_000 });
console.log(completed.text);
```

### Программный запуск MCP-сервера

```ts
import { loadConfig, createMcpServer, runTransport } from 'syntx-ai-mcp';

const config = loadConfig();          // из env
const factory = () => createMcpServer(config).server;
await runTransport(factory, 'stdio', 3000);
```

### Экспортируемые сущности SDK

| Группа | Методы |
|---|---|
| `syntx.auth` | `setToken`, `getToken`, `isAuthenticated`, `validateToken`, `logout`, `sendEmailOtp`, `verifyEmailOtp`, `loginWithEmail` |
| `syntx.ai` | `listServices`, `listModels` |
| `syntx.user` | `me`, `getBalance`, `getSubscription`, `getSettings` |
| `syntx.chats` | `list`, `create`, `getMessages`, `waitForResponse`, `pollForResponse`, `cancelMessage`, `generateTitle`, `delete`, `pin`, `moveToFolder`, `uploadFiles`, `getUploadedFiles`, `deleteFile`, `transcribe` |
| `syntx.llm` | `getLimits`, `generate`, `listModels`, `getChatStream`, `waitForResponse` |
| `syntx.design` | `generate` |
| `syntx.audio` | `listVoiceExamples` |
| `syntx.plans` | `list`, `getPromoBanners` |
| `syntx.folders` / `syntx.settings` | список папок по scope, `create({title, scope?, color?, chat_uuids?})`, `addChats(folderUuid, chatUuids)`, `delete(folderUuid)`, настройки приложения |

## Стриминг ответов

Текстовые ответы идут через `llm/*` text-flow (SSE primary + REST-поллинг как fallback). Все text-scope операции (`ask`, `stream-message`, `send-message`, `wait-for-response`) внутри используют одну и ту же связку:

```ts
// 1. Создать чат
const chat = await syntx.chats.create({ scope: 'text', title: 'Demo' });

// 2. Отправить промпт (POST /api/v1/llm/generate с chat_uuid)
await syntx.llm.generate({
  prompt: 'Расскажи о Kepler-186f',
  aiName: 'gemini',
  modelType: 'gemini-3.5-flash',
  chatUuid: chat.uuid,
});

// 3. Дождаться ответа (SSE primary, REST polling fallback)
const completed = await syntx.llm.waitForResponse(chat.uuid, {
  timeout: 60_000,
});
console.log(`\n✓ ${completed.text.length} chars`);
```

> **Как это работает:** API syntx.ai генерирует ответ асинхронно. SSE-стрим `sse.syntx.ai` отдаёт дельты текста (`event: message`, `type: content`); по `[DONE]` (или чистому закрытию) ответ считается завершённым. На сбое SSE SDK переключается на REST-поллинг через `chats.pollForResponse` — для пользователя разницы нет.

Внутри MCP-сервера инструмент `stream-message` оборачивает тот же поток, отправляя `notifications/progress` и `notifications/message` (если клиент передал `progressToken` в `_meta`).

Стратегия управляется через `SYNTX_STREAM_MODE`:

| Значение | Поведение |
|---|---|
| `auto` (по умолчанию) | SSE primary, REST-поллинг при сбое транспорта |
| `stream` | Только SSE (сбои пробрасываются вызывающему) |
| `poll` | Сразу REST-поллинг через `chats.pollForResponse` |
| `off` | Fire-and-forget: `ask` создаёт чат, отправляет промпт и сразу возвращает `chat_uuid` |

### Архитектура text-flow (`llm/*`)

```
MCP-клиент
   │ ask / stream-message / send-message / wait-for-response
   ▼
mcp/tools/chats.ts
   │ (text-scope → всегда llm/*)
   ▼
LlmResource
   │ generate → POST /api/v1/llm/generate
   │ getChatStream → GET  /api/v1/llm/chats/{id}/stream
   │ waitForResponse → openSse(...)  ──► https://sse.syntx.ai/...
   │              └ on error/timeout → chats.pollForResponse() (fallback)
```

Legacy-путь `POST /chats/{id}/messages` с `objects[]` и `model_type` удалён в v0.4.0 — для text-scope остался только `llm/*`. Не-text скоупы (image/video/audio) обслуживаются отдельными инструментами `generate-image` / `generate-audio` / `generate-video`.

Полный справочник типов — в `src/types.ts`. Внутреннее устройство слоёв — в [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).

---

## Примеры

В каталоге [`examples/`](examples) лежат готовые сценарии:

| Файл | Описание |
|---|---|
| `chat-example.ts` | Прямая работа с чатами через SDK (text-flow `llm/*`) |
| `mcp-client-example.ts` | Подключение к серверу как MCP-клиент и вызов `ask` |
| `claude-desktop-config.json` | Готовый конфиг для Claude Desktop |

Запуск примеров:

```bash
npm run build
npx tsx examples/mcp-client-example.ts
SYNTX_TOKEN=... npx tsx examples/stream-example.ts "Расскажи анекдот"
```

---

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

```bash
git clone <repo>
cd syntx-ai-mcp
npm install
npm run build        # CJS + ESM + dts (tsup)
npm run typecheck    # tsc --noEmit
npm run dev          # сборка в watch-режиме
```

**Добавление нового инструмента:**

1. Создайте файл в `src/mcp/tools/` с объектом `SyntxTool`.
2. Включите его в `src/mcp/tools/index.ts` (`allTools`).
3. Готово — сервер и `tools/list` подхватят автоматически.

Детально — в [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).

**Agent skills (каталог `skills/`).** В корне репозитория также живут [Anthropic-совместимые](https://agentskills.io/specification) skills для AI-агентов, использующих MCP: каждый skill — это папка `skills/<name>/SKILL.md` (≤ 500 строк) с YAML-frontmatter (`name`, `description`, `license`, `compatibility`, `metadata`), необязательными `references/` и `assets/`. Skill публикуется в git вместе с кодом; на машине пользователя Kilo подхватывает его после копирования в `~/.config/kilo/skills/`. Версия `metadata.version` синхронизируется с `package.json:version`.

---

## Как это работает

```
MCP-клиент (Claude/Cursor/…)
        │  stdio или HTTP+SSE
        ▼
TRANSPORT   src/transport/   ── stdio.ts · http.ts
        │  JSON-RPC
        ▼
MCP SERVER  src/mcp/         ── server.ts · registry.ts · tools/ · resources/ · prompts/
        │  вызовы SDK
        ▼
SDK         src/             ── SyntxClient · resources/ · auth
        │  fetch
        ▼
syntx.ai API   https://api.syntx.ai
```

Зависимости направлены строго вниз: транспорт зависит от MCP-ядра, ядро — от SDK, SDK — только от платформы. Ошибки API маппятся в `isError`-ответы, поэтому JSON-RPC-канал никогда не обрывается.

---

## Дорожная карта

- [x] Транскрипция аудио как инструмент (`transcribe`)
- [x] Загрузка файлов (`upload-files`) с поддержкой бинарных данных в MCP
  - [x] Стриминг ответов через SSE (см. `stream-message`, `llm.waitForResponse`, `SYNTX_STREAM_MODE`)
  - [x] CI: GitHub Actions (`npm run typecheck && npm run build` на Node 18/20/22)
  - [x] Аутентифицированный HTTP-транспорт (`MCP_HTTP_TOKEN` + Host/Origin allow-list)
  - [ ] OAuth-flow для получения токена из CLI
  - [ ] Юнит-тесты (Vitest)

---

## Сопутствующие документы

- [CHANGELOG.md](CHANGELOG.md) — история релизов.
- [CONTRIBUTING.md](CONTRIBUTING.md) — правила участия, стиль кода, советы по PR.
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) — послойное описание архитектуры.
- [`skills/`](skills/) — Anthropic-совместимые agent skills. В [`skills/syntx-ai-mcp-usage/SKILL.md`](skills/syntx-ai-mcp-usage/SKILL.md) — операционные знания для агентов, вызывающих `syntx-ai-mcp_*` инструменты (lifecycle чатов, выбор модели, recovery после timeout, security caveats).

---

## Лицензия

[MIT](LICENSE)

TDQS

B3.4/5.0

Scored across 58 tools

Disambiguation3/5

Most tools target distinct resources, but chat/message operations overlap heavily: ask, stream-message, send-message plus wait-for-response, and get-messages vs get-favorite-messages require careful reading. The detailed descriptions help, but the breadth of the surface makes misselection plausible.

Naming Consistency4/5

Names are consistently kebab-case and mostly follow a verb_noun pattern with predictable resource suffixes. Minor deviations like whoami, ask, transcribe, chat-exists, and get-inprogress keep it from being perfectly uniform.

Tool Count1/5

58 tools is far beyond a practical agent-facing surface, with many niche endpoints for trends, tariffs, token pricing, prompt enhancer, identities, and events. The broad platform scope explains some size, but the selection burden remains extreme.

Completeness4/5

The surface covers chats, messages, files, projects, agents, models, media generation, billing/limits, and auth extensively. Minor gaps remain, such as quoting LLM reset price without a corresponding reset/purchase action and limited direct chat metadata retrieval.

Maintenance

ActivityMaintained
ResponsivenessNo issues