{
  "markdown": "<p align=\"center\">\n  <img src=\"assets/banner.svg\" alt=\"kwork-mcp\" width=\"100%\">\n</p>\n\n[![CI](https://github.com/simonether/kwork-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/simonether/kwork-mcp/actions/workflows/ci.yml)\n[![PyPI](https://img.shields.io/pypi/v/kwork-mcp?color=blue&logo=pypi&logoColor=white)](https://pypi.org/project/kwork-mcp/)\n[![Python](https://img.shields.io/badge/python-3.12%E2%80%933.14-3776AB?logo=python&logoColor=white)](https://www.python.org/downloads/)\n[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\n`kwork-mcp` 1.0 — production-grade stdio MCP-шлюз к Kwork для работы из Codex. Он\nдаёт типизированные read-результаты, проверяет фактический аккаунт, координирует\nлимиты между процессами и проводит все записи через durable `prepare → commit →\nreconcile`.\n\nЭто breaking redesign. Для миграции с 0.2.x см.\n[руководство по миграции](docs/migration-1.0.md).\n\n## Что гарантирует шлюз\n\n- `structuredContent` соответствует объявленному `outputSchema`; текстовый `content`\n  сохраняет краткое резюме и JSON-копию результата.\n- Read-операции различают `known_data`, `known_empty` и `unknown_error`; ошибки\n  возвращаются с `isError=true` и стабильным кодом.\n- Перед каждым write заново проверяются `KWORK_EXPECTED_USER_ID` и фактический\n  аккаунт. Без `KWORK_ENABLE_WRITES=true` запись невозможна.\n- Точный payload, его SHA-256, TTL, confirmation token и idempotency key связаны в\n  общем SQLite ledger. Одну операцию выполняет только один процесс.\n- Неоднозначный результат записи не повторяется автоматически: состояние\n  `submission_unknown` требует `reconcile_write`.\n- Лимиты account/route, защита от burst и circuit breaker общие для всех процессов,\n  использующих один `KWORK_STATE_DIR`; fingerprint общей policy не позволяет\n  процессу с другими лимитами ослабить координацию.\n- `kwork==0.2.0` закреплён; сигнатуры и generic routes проверяются fail-loud при\n  старте и contract-тестами.\n- Token и optional proxy сохраняются в account-scoped файлах с `0700/0600`,\n  `flock`, проверкой всей ancestor chain, `O_NOFOLLOW`/FD-anchored traversal и\n  atomic replace. Runtime-discovered credentials динамически редактируются в логах\n  и внешних данных.\n- Тексты проектов, профилей, сообщений и уведомлений помечаются\n  `external_untrusted` и не являются инструкциями для агента.\n\n## Установка\n\nТребуются Python 3.12–3.14 и [uv](https://docs.astral.sh/uv/).\n\n```bash\nuvx --from kwork-mcp==1.0.0rc1 kwork-mcp-bootstrap --help\n```\n\nИз исходников:\n\n```bash\ngit clone https://github.com/simonether/kwork-mcp.git\ncd kwork-mcp\nuv sync --locked --dev\nuv run kwork-mcp-bootstrap --help\n```\n\n`kwork-mcp` использует только stdio. Все его логи идут в stderr; stdout\nзарезервирован для MCP JSON-RPC. `kwork-mcp-bootstrap` — отдельная human CLI и не\nявляется MCP transport.\n\n## Безопасная конфигурация\n\nОбычный сервер работает без login/password/token/proxy в конфигурации host.\nЕдинственный поддерживаемый production flow:\n\n1. Узнайте стабильный numeric `user_id` своего аккаунта из настроек/профиля Kwork.\n2. Один раз запустите bootstrap из настоящего terminal TTY:\n\n```bash\nKWORK_EXPECTED_USER_ID=123456 \\\n  uvx --from kwork-mcp==1.0.0rc1 kwork-mcp-bootstrap\n```\n\nCLI скрыто запросит login/password, optional phone digits и optional proxy URL,\nвызовет только auth + `get_me`, сверит точный `user_id` и атомарно запишет\naccount-bound credential record. Если существует legacy `~/.kwork_token`, CLI\nпредложит явный validated import: только regular file текущего владельца с mode\n`0600`, без symlink. Legacy-файл после успешного импорта намеренно остаётся на\nместе, чтобы удаление было отдельным осознанным действием.\n\n3. Запускайте normal MCP только с безопасными steady-state ключами:\n\n```bash\nexport KWORK_EXPECTED_USER_ID='123456'\nexport KWORK_PERSIST_TOKEN='true'\nexport KWORK_ENABLE_WRITES='false'\nuvx --from kwork-mcp==1.0.0rc1 kwork-mcp\n```\n\nNormal entrypoint fail-closed отклоняет `KWORK_LOGIN`, `KWORK_PASSWORD`,\n`KWORK_TOKEN`, `KWORK_PHONE_LAST` и `KWORK_PROXY_URL`, даже если они пришли через\nenvironment. Не помещайте эти значения в Codex/Claude MCP config: некоторые hosts\nвстраивают env map в собственный process argv. `.env` из cwd никогда не\nзагружается. Secret values не принимаются через argv.\n\nПосле запуска вызовите `account_status` и сверьте `user_id`. Только затем включайте\n`KWORK_ENABLE_WRITES=true`. `KWORK_EXPECTED_USERNAME` — дополнительная, более\nхрупкая проверка: username может быть переименован, primary identity — numeric ID.\n\nПо умолчанию состояние хранится в\n`$XDG_STATE_HOME/kwork-mcp` либо `~/.local/state/kwork-mcp`. Это каталог с токенами\nи `coordination.sqlite3`; все процессы одного аккаунта должны использовать один\nлокальный `KWORK_STATE_DIR` и одинаковые shared rate/circuit/write settings.\nНесовместимый fingerprint отклоняется fail-loud. Файлы содержат чувствительные\nданные и не зашифрованы самим приложением — используйте защищённую учётную запись\nОС и шифрование диска. Вся физическая ancestor chain должна принадлежать текущему\nuser либо root и не быть group/other-writable. Разрешён один стандартный sticky\ntemp boundary (например, `/tmp`), после которого gateway создаёт private `0700`\nкаталог; обычный `0777` parent, чужой owner, final symlink или подмена компонента\nотклоняются.\nВерсия 1.0 использует POSIX `fcntl`/`flock` и поддерживает Linux/macOS, но не\nWindows.\n\nOptional proxy вводится только bootstrap-команде и сохраняется рядом с token в\nзащищённом account record; normal server не принимает `KWORK_PROXY_URL`. Legacy\nrecord без proxy означает прямое подключение. Чтобы добавить, заменить или удалить\nproxy либо обновить истёкшую сессию, остановите процессы этого account/state,\nповторите bootstrap и перезапустите MCP. Файл защищён правами ОС, но не шифруется\nна уровне приложения.\n\nПолный справочник: [docs/configuration.md](docs/configuration.md).\n\n## Подключение к Codex\n\nСначала выполните bootstrap в обычном терминале, как показано выше. Затем добавьте\nв `~/.codex/config.toml` только безопасные значения:\n\n```toml\n[mcp_servers.kwork]\ncommand = \"uvx\"\nargs = [\"--from\", \"kwork-mcp==1.0.0rc1\", \"kwork-mcp\"]\n\n[mcp_servers.kwork.env]\nKWORK_EXPECTED_USER_ID = \"123456\"\nKWORK_PERSIST_TOKEN = \"true\"\nKWORK_ENABLE_WRITES = \"false\"\n```\n\nДля локальной checkout-версии:\n\n```toml\n[mcp_servers.kwork]\ncommand = \"uv\"\nargs = [\"--directory\", \"/absolute/path/to/kwork-mcp\", \"run\", \"kwork-mcp\"]\n\n[mcp_servers.kwork.env]\nKWORK_EXPECTED_USER_ID = \"123456\"\nKWORK_PERSIST_TOKEN = \"true\"\nKWORK_ENABLE_WRITES = \"false\"\n```\n\nCodex CLI, IDE extension и desktop app используют общую MCP-конфигурацию host.\nПосле изменения перезапустите соответствующий клиент и вызовите `account_status`.\nНикогда не добавляйте туда token/login/password/phone/proxy — ни как `env`, ни как\n`env_vars`, ни как arguments.\n\n## MCP tools\n\n### Read-only\n\n| Tool | Результат |\n|---|---|\n| `account_status` | Фактический account ID, binding и готовность writes |\n| `get_connects` | Активные и общие коннекты |\n| `get_user_info`, `search_users` | Профиль/поиск пользователей |\n| `discover_projects` | `favorites`, `all` или `category_ids`, фильтры и opaque cursor |\n| `get_project`, `get_exchange_info` | Проект и полная exchange-информация |\n| `list_my_offers`, `get_offer` | Офферы с обязательными `offer_id` и `project_id` |\n| `list_worker_orders`, `get_order_details` | Заказы продавца и полные details |\n| `list_dialogs`, `get_dialog` | Диалоги и сообщения |\n| `list_my_kworks`, `get_kwork_details` | Собственные кворки |\n| `list_categories`, `list_favorite_categories` | Категории |\n| `list_notifications` | Полные группы уведомлений |\n\n`discover_projects` не смешивает режимы:\n\n- `favorites` — избранные категории аккаунта;\n- `all` — вся биржа;\n- `category_ids` — обязательный непустой список ID.\n\nВозвращаемый `PageInfo` содержит `next_cursor`, `query_fingerprint` и\n`high_watermark`. Cursor подписан и привязан к подтверждённому аккаунту и точным\nфильтрам. Watermark позволяет клиенту вести локальную точку наблюдения для\nбудущего delta polling, но 1.0 не обещает отдельный delta endpoint.\n\n### Safe write-flow\n\nПоддерживаемые `request.action`: `submit_offer`, `delete_offer`, `send_message`,\n`edit_message`, `delete_message`, `mark_dialog_read`, `submit_order_approval`,\n`set_kwork_state`.\n\n1. Вызовите `prepare_write` с точным request и собственным стабильным\n   `idempotency_key`.\n2. Проверьте возвращённые `payload`, `payload_hash`, account ID и `expires_at`.\n3. Передайте неизменённые `write_id`, `payload_hash` и `confirmation_token` в\n   `commit_write`.\n4. Если state равен `submission_unknown`, не вызывайте commit повторно. После\n   visibility window вызовите `reconcile_write(write_id)`.\n5. `get_write_status` читает durable ledger без remote write.\n\nПример payload для подготовки оффера:\n\n```json\n{\n  \"request\": {\n    \"action\": \"submit_offer\",\n    \"project_id\": 123,\n    \"title\": \"Точное название предложения\",\n    \"description\": \"Описание длиной не менее 150 символов, соответствующее проекту и не содержащее секретов.\",\n    \"price\": 10000,\n    \"duration_days\": 5\n  },\n  \"idempotency_key\": \"project-123-offer-v1\"\n}\n```\n\nRemote write никогда не retry автоматически. Повторный `prepare_write` с тем же\nidempotency key и другим request возвращает `idempotency_conflict`; пока исходная\nзапись остаётся `prepared`, точный replay того же request возвращает ту же запись и\nтот же HMAC-derived confirmation token. Это позволяет безопасно восстановиться\nпосле потери ответа `prepare`, не создавая второй intent. После claim/terminal state\nconfirmation token больше не выдаётся; состояние читается через\n`get_write_status`.\n\n## Модель результата и ошибок\n\nКаждый tool возвращает envelope версии `1.0`:\n\n```json\n{\n  \"schema_version\": \"1.0\",\n  \"knowledge_state\": \"known_data\",\n  \"summary\": \"…\",\n  \"data\": {},\n  \"error\": null,\n  \"meta\": {\n    \"source\": \"kwork\",\n    \"content_trust\": \"external_untrusted\",\n    \"observed_at\": \"…\",\n    \"correlation_id\": \"…\",\n    \"upstream_contract\": \"kwork==0.2.0\"\n  }\n}\n```\n\nКоды ошибок и retry/reconciliation semantics описаны в\n[docs/security.md](docs/security.md#error-taxonomy).\nНеизвестное имя tool является protocol-level JSON-RPC `-32602`, а не обычным\n`isError` business-result; имя из недоверенного запроса намеренно не отражается в\nсообщении.\n\n## Архитектура и границы\n\nШлюз отвечает за MCP transport, авторизацию Kwork, account binding, корректность\nupstream-контракта, типизацию данных и безопасную доставку write-запроса. Он\nнамеренно не содержит скоринг проектов, Notion, Telegram, email, CRM и другую\npipeline/business logic.\n\nMCP Tasks отключены. Стабильная спецификация считает их экспериментальными, а\nCodex-клиенту для коротких Kwork API-вызовов durable task lifecycle не даёт пользы.\nDurability write-flow реализована внутри ledger и доступна обычными tools без\nнестабильного protocol surface.\n\nПодробнее: [архитектура](docs/architecture.md) и\n[security model](docs/security.md).\n\n## Разработка\n\n```bash\nuv sync --locked --dev\nuv run ruff check .\nuv run ruff format --check .\nuv run mypy\nuv run pytest tests/ -v --cov=kwork_mcp --cov-report=term-missing\nuv build\nuv run twine check dist/*\nuv run check-wheel-contents dist/*.whl\n```\n\nCoverage gate — 92% branch-aware покрытия. CI дополнительно проверяет Python\n3.12–3.14, зависимости, секреты, pinned MCP Registry schema, wheel install smoke\nи согласованность версий.\n\n## Лицензия\n\n[MIT](LICENSE)\n\n<!-- mcp-name: io.github.simonether/kwork-mcp -->\n",
  "bytes": 11426,
  "sha": "8bb2b2a2ab2a1d7418e711c1f58badb597fbfc07c037b741e673585219ad1f8c",
  "repo_slug": "simonether/kwork-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_simonether_kwork_mcp_dcf3f84a/readme"
}