Metadata-Version: 2.3
Name: askia-mcp
Version: 0.2.0
Summary: MCP stdio bridge to the Askia partner API
Requires-Dist: httpx==0.28.1
Requires-Dist: mcp==2.3.0
Requires-Dist: pydantic-settings==2.16.2
Requires-Python: >=3.13
Description-Content-Type: text/markdown

# Аския MCP для веб-мастера

Настоящий MCP-сервер через **stdio**, официальный Python SDK **mcp 2.3.0**. Работает из Codex, Claude и других MCP-хостов. Вызывает партнёрский REST API Аскии; права на каждого клиента и сайт проверяет API. Это локальный процесс с HTTPS-подключением к API, не удалённый OAuth-сервер.

## Подключить

1. В кабинете веб-мастера откройте **Агенты и MCP** (`/partner/agents`). Создайте отдельный ключ для устройства. Скопируйте его из одноразового диалога. Список показывает название, дату создания и последнего обращения. Кнопка «Отозвать» сразу блокирует ключ. Ключ загрузки сайта `alit_site_…` для MCP не подходит.
2. Установите [uv](https://docs.astral.sh/uv/getting-started/installation/). MCP-хост должен видеть `uvx` в PATH; для приложения с графическим интерфейсом можно указать абсолютный путь из `command -v uvx`.
3. Передайте ключ через окружение MCP-хоста `ASKIA_MCP_TOKEN` **или** `ASKIA_MCP_TOKEN_FILE` с абсолютным путём к файлу ключа. На macOS/Linux файл должен иметь права `600`. Файл находится вне репозитория, MCP-хост читает его при запуске. Не вставляйте ключ в разговор с агентом, аргументы tools или конфиг в git. После ротации перезапустите MCP-сервер.
4. Для загрузки архива задайте `ASKIA_MCP_ARCHIVE_ROOTS` как JSON-массив абсолютных разрешённых папок, например `["/Users/me/Projects/client-site/dist"]`. По умолчанию чтение архивов запрещено. Символические ссылки за пределы папок отклоняются.

Готовый wheel версии 0.1.0 будет опубликован вместе с API по неизменяемому адресу:
`https://askia.ru/downloads/askia_mcp-0.1.0-py3-none-any.whl`.
До его публикации используйте локальный запуск ниже. Пакет не заявлен как опубликованный на PyPI.

### Codex

Добавьте в свою конфигурацию Codex (не в репозиторий):

```toml
[mcp_servers.askia]
command = "uvx"
args = ["--python", "3.13", "--from", "https://askia.ru/downloads/askia_mcp-0.1.0-py3-none-any.whl", "askia-mcp"]
env_vars = ["ASKIA_MCP_TOKEN", "ASKIA_MCP_TOKEN_FILE", "ASKIA_MCP_ARCHIVE_ROOTS"]

[mcp_servers.askia.env]
ASKIA_MCP_API_URL = "https://api.askia.ru/api/v1"
```

`env_vars` передаёт выбранные переменные из окружения Codex. В приложении, запущенном без shell-окружения, можно вместо него указать **путь** к закрытому файлу через `ASKIA_MCP_TOKEN_FILE` в `[mcp_servers.askia.env]`; сырой ключ туда не нужен. Архивные корни также можно задать в этом блоке строкой JSON.

Локальный запуск из checkout до публикации wheel:

```toml
[mcp_servers.askia]
command = "uv"
args = ["--directory", "/absolute/path/askia/mcp-server", "run", "--frozen", "askia-mcp"]
env_vars = ["ASKIA_MCP_TOKEN", "ASKIA_MCP_TOKEN_FILE", "ASKIA_MCP_ARCHIVE_ROOTS"]
```

При установке из wheel прямые зависимости закреплены точными версиями в метаданных. При запуске из checkout `uv run --frozen` использует полный `uv.lock` с транзитивными зависимостями. Проверенная команда CLI: `codex mcp add askia -- uv --directory /absolute/path/askia/mcp-server run --frozen askia-mcp`; для передачи env и архивов используйте блок выше. Инструкция соответствует [официальной документации Codex MCP](https://developers.openai.com/codex/mcp/).

### Другие stdio MCP-хосты

Общий JSON-пример, в том числе для клиентов с форматом `mcpServers`:

```json
{
  "mcpServers": {
    "askia": {
      "command": "/absolute/path/to/uvx",
      "args": ["--python", "3.13", "--from", "https://askia.ru/downloads/askia_mcp-0.1.0-py3-none-any.whl", "askia-mcp"],
      "env": {
        "ASKIA_MCP_API_URL": "https://api.askia.ru/api/v1",
        "ASKIA_MCP_TOKEN_FILE": "/absolute/private/path/askia-token",
        "ASKIA_MCP_ARCHIVE_ROOTS": "[\"/absolute/path/project/dist\"]"
      }
    }
  }
}
```

В Claude Desktop и CLI используйте настройки MCP своего клиента, выбрав stdio и эти `command`, `args`, `env`. Формат расположения файла зависит от клиента; этот пакет не меняет пользовательские настройки автоматически. До публикации wheel замените команду на `uv` и args на локальный пример выше.

## Рабочие сценарии

**Сайт из архива:** `whoami` → `list_clients`/`create_client` → `list_sites`/`create_custom_site` → агент собирает проект **локально** → `upload_static_archive` → `preview_upload` → человек просматривает ссылку → `publish_upload(confirm_publish=true)` → `get_job`. Сохраняйте `idempotency_key` публикации, чтобы при неизвестном результате проверить задачу и безопасно повторить тот же запрос. `list_uploads` показывает историю; `publish_upload` с ID сохранённого старого архива выполняет откат. Если файлы старой версии очищены, API вернёт `409 upload_missing`.

**Готовая статика в GitHub:** `get_source` → `configure_git_source(expected_revision=…)` → `fetch_git_source` → `get_source_fetch` до `ready` → `preview_upload(upload_id)` → отдельный `publish_upload`. Поддерживается публичный HTTPS GitHub-репозиторий с готовыми файлами. Точный commit SHA остаётся в результате fetch. `npm install`, произвольные сборки на сервере, shell и передача Git-токена в tool не поддерживаются. Приватный Git можно подключить отдельно в кабинете, затем MCP вызывает fetch без передачи реквизитов. Репозиторий с исходниками Vite/React сначала собирайте у себя или в CI и передавайте `dist` архивом.

**CMS:** `list_templates` → `create_template_site` → `get_content` → `update_content(expected_version_id, content)` → `preview_site` → `get_job` → после просмотра `publish_site(confirm_publish=true)`. Для отката есть `list_versions` и `rollback_site`. API атомарно проверяет expected_version_id при сохранении. Если черновик изменился, ответ draft_changed сохраняет чужую правку; агент заново читает контент и согласует изменение.

Все 25 tools имеют MCP annotations. Чтение и превью не публикуют сайты. Публикация и откат помечены как изменяющие публичный сайт и требуют `confirm_publish=true`. Это выражает намерение вызова; подтверждение пользователя остаётся обязанностью MCP-хоста. Проверка домена и подключение уже существующего домена не покупают домен. В MCP нет платежей, удаления сайтов и выдачи ключей.

## Границы и ошибки

- Архивы ZIP/TAR.GZ/TGZ до 50 МБ, только внутри разрешённых оператором папок. Сервер API дополнительно проверяет содержимое и квоты.
- Новый staged API `/sites/{id}/uploads/draft` обязателен. Если API ещё старый, получите 404; автоматического перехода на старый auto-publish `/upload` нет. Git также требует нового source API.
- API по умолчанию `https://api.askia.ru/api/v1`; HTTPS-хост должен входить в `ASKIA_MCP_ALLOWED_API_HOSTS` (JSON-массив). Безопасный локальный dev HTTP разрешается только с `ASKIA_MCP_ALLOW_LOCAL_HTTP=true` на loopback. Редиректы запрещены, TLS проверяется. Произвольного URL-tool нет.
- Таймаут 30 с, ограниченный размер ответа, нет автоматических повторов записи. `result_unknown` означает: запрос мог выполниться; сначала проверьте сайт/историю/задачу. Ошибки API возвращают код, без сырых тел и внутренних трассировок. Ключ удаляется из ответов, в stdout только MCP.
- Партнёрский ключ может использоваться напрямую с REST API в пределах роли партнёра. Отсутствие покупок в MCP не сужает права самого API-ключа. Давайте агентам отдельные ключи и отзывайте ненужные.

## Разработка и проверка

```sh
uv sync --frozen
uv run pytest -q
uv run ruff check .
uv run ruff format --check .
uv run pyright
uv build --wheel
```

Тесты запускают официальный MCP `ClientSession` по stdio к отдельному subprocess, затем реальный HTTP к fixtures: схема tools, изоляция сайтов, staged upload → preview → explicit publish, Git → staged, safe errors и запрет автоматических покупок. Секреты тестовые.

`uv run python scripts/smoke_local_api.py` проверяет отдельный локальный API в **dev** на `127.0.0.1:18110`, реальную сборку CMS worker и stdio. Создаёт тестовый бизнес и сайт; временный ключ отзывается в `finally`. Не запускайте на проде. `scripts/smoke_wheel.py /absolute/path/dist/askia_mcp-0.1.0-py3-none-any.whl` проверяет установку и stdio tools через `uvx` без живых ключей.

SDK: [официальная документация Python MCP v2](https://py.sdk.modelcontextprotocol.io/), зависимости закреплены в `pyproject.toml` и `uv.lock`.
