# MCP-сервер ПромтЛаб ПромтЛаб работает как [MCP-сервер](https://modelcontextprotocol.io/) — ваши промпты доступны прямо из ИИ-клиентов (Claude Code, Cursor, Windsurf и др.). **Версия сервера:** `v1.2.0` (см. `server.json`). В v1.2 добавлены 5 tools: `list_teams`, `whoami`, `list_trash`, `restore_prompt`, `purge_prompt`; квотирование покрыто 13 из 30 tools. ## Быстрый старт ### 1. Создайте API-ключ Откройте **Настройки → API-ключи → Создать**. Скопируйте ключ — он показывается один раз. ### 2. Подключите MCP-сервер #### Claude Code ```bash claude mcp add promptvault --transport http https://ваш-домен/mcp --header "Authorization: Bearer pvlt_ваш_ключ" ``` Или добавьте в `~/.claude/claude_desktop_config.json`: ```json { "mcpServers": { "promptvault": { "url": "https://ваш-домен/mcp", "headers": { "Authorization": "Bearer pvlt_ваш_ключ" } } } } ``` #### Cursor Откройте **Settings → MCP Servers → Add Server**: - Name: `promptvault` - URL: `https://ваш-домен/mcp` - Headers: `Authorization: Bearer pvlt_ваш_ключ` #### Другие клиенты Любой клиент с поддержкой MCP Streamable HTTP: - URL: `https://ваш-домен/mcp` - Аутентификация: `Authorization: Bearer pvlt_ваш_ключ` Для локальной разработки замените `https://ваш-домен` на `http://localhost:8080`. ## Возможности ### Tools (30 шт.) #### Чтение (14) | Tool | Описание | Viewer | |------|----------|--------| | `whoami` | Текущий пользователь (id, email, plan) | ✅ | | `search_prompts` | Поиск по промптам, коллекциям, тегам (топ-K, без курсора) | ✅ | | `search_suggest` | Автодополнение по префиксу | ✅ | | `list_prompts` | Список промптов с фильтрами и **cursor pagination** (см. ниже) | ✅ | | `get_prompt` | Получить промпт по ID с полным содержимым | ✅ | | `list_prompt_vars` | Извлечь `{{переменные}}` из промпта (для `use_prompt`) | ✅ | | `prompt_list_pinned` | Список закреплённых промптов | ✅ | | `prompt_list_recent` | Список недавно использованных промптов | ✅ | | `list_collections` | Список коллекций с количеством промптов | ✅ | | `collection_get` | Получить коллекцию по ID | ✅ | | `list_tags` | Список тегов | ✅ | | `list_teams` | Список команд пользователя (id, role, member_count) | ✅ | | `list_trash` | Содержимое корзины (soft-deleted промпты, пагинация) | ✅ | | `get_prompt_versions` | История версий промпта | ✅ | #### Запись (11) | Tool | Описание | Viewer | Ест квоту | |------|----------|--------|-----------| | `create_prompt` | Создать промпт | ❌ | ✅ | | `update_prompt` | Обновить промпт (создаёт новую версию) | ❌ | ✅ | | `prompt_favorite` | Переключить статус избранного | ❌ | — | | `prompt_pin` | Закрепить/открепить промпт (team_wide для команды) | ❌ | — | | `prompt_revert` | Откатить промпт к предыдущей версии | ❌ | ✅ | | `prompt_increment_usage` | Отметить использование промпта (для аналитики) | ❌ | — | | `share_create` | Создать публичную ссылку на промпт | ❌ | ✅ | | `restore_prompt` | Восстановить промпт из корзины | ❌ | ✅ | | `collection_update` | Обновить название/описание/цвет/иконку коллекции | ❌ | ✅ | | `create_tag` | Создать тег | ❌ | ✅ | | `create_collection` | Создать коллекцию для организации промптов | ❌ | ✅ | #### Удаление (5) | Tool | Описание | Viewer | Ест квоту | |------|----------|--------|-----------| | `delete_prompt` | Удалить промпт (в корзину на 30 дней) | ❌ | ✅ | | `delete_collection` | Удалить коллекцию (промпты внутри не затрагиваются) | ❌ | ✅ | | `tag_delete` | Удалить тег (промпты не затрагиваются) | ❌ | ✅ | | `share_deactivate` | Деактивировать публичную ссылку | ❌ | ✅ | | `purge_prompt` | Удалить промпт навсегда (из корзины, необратимо) | ❌ | ✅ | ### Квотирование Из 30 tools дневную MCP-квоту едят только **13** write/destructive операций, перечисленные выше с ✅ в колонке «Ест квоту». Остальное (14 read-only + 3 UX-toggle: `prompt_favorite`, `prompt_pin`, `prompt_increment_usage`) — бесплатно. Лимиты по тарифам: Free — 5 платных вызовов в день, Pro — 30, Max — безлимит. При превышении backend возвращает `402 Payment Required`. ### Resources | URI | Описание | |-----|----------| | `promptvault://collections` | Все коллекции (контекст для LLM) | | `promptvault://tags` | Все теги (контекст для LLM) | | `promptvault://prompts/{id}` | Конкретный промпт по ID | ### Prompts | Имя | Аргументы | Описание | |-----|-----------|----------| | `use_prompt` | `id` (required), `vars` (JSON object, optional), `role` (user/assistant, optional) | Загрузить промпт, подставить `{{переменные}}` и вернуть как сообщение | **Синтаксис переменных** — `{{имя}}`. Если промпт содержит переменные, но `vars` не передан, сервер вернёт ошибку со списком недостающих и JSON-скелетом. Пример: ```json { "id": "42", "vars": "{\"язык\":\"Go\",\"задача\":\"рефакторинг\"}", "role": "user" } ``` ### Autocomplete (completions/complete) MCP-клиенты с поддержкой `completion/complete` (Claude Code, Cursor) получают подсказки для `use_prompt`: - `id` — список ID ваших промптов с префиксным поиском (до 20). - `role` — `user` / `assistant`. - `vars` — JSON-скелет с нужными переменными (требует `id` в context). ## Работа с командами Все tools поддерживают параметр `team_id` для работы в командном пространстве. Без `team_id` — личное пространство. ``` "Найди мой промпт для код-ревью в команде" → search_prompts(query="код-ревью", team_id=2) "Создай промпт в командном пространстве" → create_prompt(title="...", content="...", team_id=2) ``` ### Ролевые ограничения | Роль | Чтение | Запись | |------|--------|--------| | **owner** | ✅ | ✅ | | **editor** | ✅ | ✅ | | **viewer** | ✅ | ❌ | Viewer имеет доступ только к read-tools: `search_prompts`, `search_suggest`, `list_prompts`, `get_prompt`, `prompt_list_pinned`, `prompt_list_recent`, `list_collections`, `collection_get`, `list_tags`, `get_prompt_versions`. ## Примеры использования ### Поиск и получение промпта ``` "Найди промпты про TypeScript" → search_prompts(query="TypeScript") → get_prompt(id=42) ``` ### Создание промпта с тегами и коллекцией ``` "Создай промпт для рефакторинга кода" → list_tags() # получить доступные теги → list_collections() # получить доступные коллекции → create_prompt( title="Рефакторинг кода", content="Ты — эксперт по рефакторингу...", tag_ids=[1, 3], collection_ids=[2] ) ``` ### Использование prompt-ресурса ``` "Используй мой промпт #5 для текущей задачи" → use_prompt(id="5") # LLM получает отформатированный промпт: "# Заголовок\n\nСодержимое" ``` ### История версий и откат ``` "Покажи историю изменений промпта #10" → get_prompt_versions(prompt_id=10) "Откати промпт #10 к версии #3" → prompt_revert(prompt_id=10, version_id=3) ``` ### Управление избранным и закреплением ``` "Добавь промпт #5 в избранное" → prompt_favorite(id=5) "Закрепи промпт #5 для всей команды" → prompt_pin(id=5, team_wide=true) "Покажи мои закреплённые промпты" → prompt_list_pinned() ``` ### Шаринг ``` "Поделись промптом #5" → share_create(prompt_id=5) # → { url: "https://promtlabs.ru/s/abc123" } "Отключи ссылку на промпт #5" → share_deactivate(prompt_id=5) ``` ## API-ключи (v1.1+) - Максимум **5 ключей** на пользователя - Ключ показывается **один раз** при создании - Управление: **Настройки → API-ключи** - Формат: `pvlt_` + 43 символа ### Scope-ограничения При создании ключа можно задать ограничения: | Ограничение | Эффект | |---|---| | **Read-only** | Ключ может только читать (10 read-tools), write/delete — `"scope denied"`. | | **Команда (team_id)** | Ключ работает только с ресурсами выбранной команды. Запросы к другой команде/личному пространству — `"team mismatch"`. | | **Разрешённые tools** | Белый список имён tool'ов. Вызовы вне списка — `"scope denied"`. | | **Срок действия (expires_at)** | После даты сервер возвращает `"unauthorized"` на любом запросе. | Backward-compat: ключи, созданные до v1.1, получают полный доступ (как раньше). ## Cursor pagination (list_prompts) `list_prompts` поддерживает keyset-курсор для выкачивания большой библиотеки. **Input:** - `limit` *(int, default 50, max 200)* — размер страницы. - `cursor` *(string, optional)* — opaque метка из `next_cursor` предыдущего ответа. **Output:** - `prompts` — массив промптов. - `total` — общее количество (считается на каждом запросе). - `next_cursor` — метка для следующей страницы. Пусто/отсутствует = последняя страница. **Семантика смены фильтра:** cursor содержит хеш фильтра. Если клиент меняет фильтр между страницами — сервер возвращает `cursor_filter_mismatch`, клиент должен перезапустить пагинацию без cursor. Legacy `page`/`page_size` остаются для обратной совместимости (используются, если `cursor` не передан). Для постраничного поиска промптов — `list_prompts(query=..., cursor=...)`. `search_prompts` возвращает топ-K агрегированных результатов (prompts+collections+tags) без курсора. ## Resource subscriptions (v1.1+) MCP-клиенты могут подписаться на изменения ресурсов через стандартный `resources/subscribe`: | URI | Событие `resources/updated` | |---|---| | `promptvault://collections` | create/update/delete коллекции **через MCP** | | `promptvault://tags` | create/delete тега **через MCP** | | `promptvault://prompts/{id}` | update/delete/favorite/pin/revert/share этого промпта **через MCP** | **Known Gap:** при изменениях **через веб-интерфейс или HTTP API** MCP-уведомления пока не отправляются (cross-channel broadcast отложен). Клиент увидит свежие данные при следующем `resources/read` или своей CUD-операции. ## Лимиты - **120 запросов/мин** на IP - **60 запросов/мин** на пользователя - Максимум **100 записей** на страницу в list-операциях ## Переменные окружения ```bash MCP_ENABLED=true # включить MCP-сервер (по умолчанию false) MCP_MAX_KEYS_PER_USER=5 # лимит ключей на пользователя ``` ## Troubleshooting ### "unauthorized" - Проверьте формат заголовка: `Authorization: Bearer pvlt_ваш_ключ` - Убедитесь, что ключ не отозван (Настройки → API-ключи) - Проверьте, что MCP включён (`MCP_ENABLED=true`) ### "read-only access" - У вас роль **viewer** в команде — запись недоступна - Попросите owner/editor повысить вашу роль ### "too many requests" - Превышен лимит: 120 req/мин на IP или 60 req/мин на пользователя - Заголовок `Retry-After: 60` указывает время ожидания ### Нет подключения - Проверьте URL: `/mcp` (не `/api/mcp`) - Убедитесь, что протокол правильный (HTTP vs HTTPS) - Для локальной разработки: `http://localhost:8080/mcp`