Починить нерабочий пакет, привести запросы к API 2.4 и добавить каскадную модель инструментов - #1
Починить нерабочий пакет, привести запросы к API 2.4 и добавить каскадную модель инструментов#1mazixs wants to merge 11 commits into
Conversation
…3.11
Без верхней границы `mcp>=1.0` резолвился в mcp 2.0.0 (28.07.2026) — мажорную
переработку SDK, где low-level Server принимает handlers в конструкторе вместо
декораторов. Опубликованный пакет падал при импорте:
AttributeError: 'Server' object has no attribute 'list_tools'
Unit-тесты этого не ловили (server.py покрыт на 0%), поэтому в CI добавлен шаг
реального MCP-хендшейка через scripts/smoke.py --no-api.
- mcp>=1.28,<2, httpx>=0.28,<0.29, python-dotenv>=1.0,<2
- dev: pytest 9.x, pytest-asyncio 1.x, ruff 0.16.x, pytest-cov
- requires-python >=3.11 (3.10 EOL 31.10.2026), матрица CI 3.11–3.14
- asyncio_default_fixture_loop_scope=function под pytest-asyncio 1.x
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…API 2.4 SDK 2.x: обработчики передаются в конструктор Server, результаты собираются явно, типы протокола переехали в mcp_types со snake_case полями. Сервер теперь собирается фабрикой build_server() — это и сделало MCP-слой тестируемым. Исправления запросов (сверено со страницами методов checko.ru/integration/api): - /entrepreneur получал `ogrnip`, а API ждёт `ogrn` — поиск ИП по ОГРНИП не работал - добавлен инструмент get_enforcements (/enforcements, ФССП): метод существует с версии 2.0, но в проекте не было ни одного упоминания - ИНН 12 цифр и ОГРНИП 15 цифр больше не отвергаются там, где API их принимает: legal-cases, contracts, inspections, timeline, fedresurs, bankruptcy-messages - /search: открыты founder-name, leader-name, reg-date, upd-date и obj=ent, добавлены okved, opf, active; убраны date_from/date_to, которых у метода нет - /contracts: добавлено значение law=94 и параметр sort (включая -price) - /inspections: добавлен sort - /company: добавлен okpo Клиент: httpx2, повторы на 429 и 5xx с backoff и Retry-After, непустой текст сетевых ошибок, учёт meta.balance. Валидация аргументов идёт до создания клиента, поэтому невалидный вызов не расходует квоту и не маскируется ошибкой ключа. Ошибки инструментов помечаются is_error=True, успешные ответы дублируются в structured_content, инструменты получили title и read-only annotations. Тесты: 83 → 159, покрытие 75% → 94%, server.py 0% → 93%. Добавлены tests/test_api_contract.py (сверка схем с документированными параметрами и проверка фактически уходящего запроса) и tests/test_server.py (настоящая MCP-сессия в памяти). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…равка справочника README: раздел установки переписан на четыре шага — ключ, uv, одна команда под выбранный клиент (глобально и для проекта отдельно), проверка. Синтаксис сверён с официальной документацией каждого клиента. Ссылки переведены с апстрима на форк: пакет checko-mcp на PyPI принадлежит исходному проекту, поэтому `uvx checko-mcp` без `--from` поставил бы не этот код. agent-guide.md (отдаётся агенту как ресурс checko://docs/agent-guide) содержал те же ошибки, что и код, то есть активно вводил агента в заблуждение: - утверждал, что поиск по ФИО невозможен, хотя search by=founder-name и by=leader-name — основной способ раскрутить связи человека без ИНН - указывал параметр ogrnip и законы только 44/223 - не упоминал исполнительные производства ФССП Добавлены: предупреждение про объём ответа при source=true, разница между иском и исполнительным производством, отсутствие белорусских данных (УНП) в API. docs/api: исправлены entrepreneur.md (ogrn вместо ogrnip), search.md (полный набор критериев, убраны несуществующие date_from/date_to), contracts.md (law=94, sort); добавлена enforcements.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Замеры на реальном API показали, что перерасход контекста — не теоретическая
проблема, а норма. Claude Code предупреждает на 10 000 токенах и обрезает
на 25 000; из 21 проверенного ответа порог обрезки превышали шесть:
/finances?extended=true 151 597 символов (~50 500 токенов)
/search?by=name 108 191 (~36 000)
/company (крупная организация) 113 081 (~37 700)
/contracts?law=94 92 470 (~30 800)
/search?by=reg-date 120 352 (~40 100)
/enforcements 59 106 (~19 700)
Обрезка молчаливая: агент получает оборванный JSON и достраивает домыслом.
Сжатие сделано универсальным свёртыванием длинных списков, а не белым списком
полей: белый список молча терял бы поля, которые Checko добавит в новой версии
API, а скалярные поля несут именно факторы риска. Исключение — /finances, где
в сжатом виде остаются ключевые строки отчётности за все доступные годы.
Результат на тех же ответах: сжатие в 4,5–8,4 раза, максимум 7 053 токена —
ни один ответ больше не достаёт даже до порога предупреждения. Факторы риска,
статус и сводные показатели (ЗапВсего, ОбщСуммИск, ОбщКолич, ОбщСум, ОстЗадолж)
сохраняются полностью; в блоке `сжатие` указано, сколько элементов было всего
и как получить полный ответ.
source=true теперь требует detail="full" явно: сворачивать запрошенный дамп ФНС
бессмысленно.
Исправлено по итогам живой проверки — реальные имена полей не совпадали
с документацией:
- /enforcements: ИспПрНомер, ПредмИсп, СумДолг, ОстЗадолж и сводные ОбщКолич,
ОбщСум, ОстЗадолж вместо выдуманных Номер, ПредметИсп, СуммаДолг, Остаток
- /legal-cases в agent-guide: Записи[] с Номер, Дата, Ист, Ответ, ПрАктуал,
ПрАктив вместо несуществующих СписДел[] с НомерДела, Истец, Ответчик, Стадия
- /company: Учред.РосОрг/ИнОрг/ПИФ вместо Учред.ЮЛ, РМСП вместо МСП.Кат
- правило про квоту было неверным: на бесплатном тарифе meta.balance равен 0
постоянно, и 100 запросов в сутки при этом работают. Ориентир —
meta.today_request_count
- на белорусский УНП API отвечает 200 с пустым data, а не ошибкой: агент мог
принять это за «компания не найдена»
Тесты: 159 → 193, покрытие 95%.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Проблема была не в числе инструментов, а в том, что 13 тонких обёрток заставляли агента самому знать модель данных Checko: какой метод вызвать по числу цифр в ИНН, в каком порядке опросить шесть реестров, что не забыть про исполнительные производства. Каждый шаг — место для ошибки и лишний расход контекста. Новая поверхность (13 инструментов, три уровня): - resolve — вход по наименованию, ФИО или любому идентификатору, возвращает короткий список кандидатов вместо 100 полных записей поиска - profile — карточка с автоопределением вида субъекта по числу цифр: 8 ОКПО, 9 БИК, 10 ИНН юрлица, 12 ИНН физлица, 13 ОГРН, 15 ОГРНИП. Для 12 цифр сначала ЕГРИП, и только при отсутствии ИП — данные физлица, о чём сказано в ответе - due_diligence_report — шесть реестров параллельно, 5–6 запросов - bankruptcy_risk — четыре источника сигналов, 4–5 запросов - девять обёрток над отдельными методами остались для детализации Заменены: search, get_company, get_entrepreneur, get_person. Отчёты считают сигналы по формальным правилам с указанием источника и уровня, но вердикт о сделке не выносят: правила проверяемы, суждение — нет. Кэш ответов с TTL (CHECKO_CACHE_TTL, по умолчанию 900 с): повторный отчёт по тому же субъекту стоит 1 запрос вместо 5. Ошибки не кэшируются. Проверено на живом API: - due_diligence_report по крупной организации — 6 запросов, 4 295 символов (~1 431 токен) на всю проверку. Только сырая карточка той же организации — 113 081 символ. Сигналы найдены верно: санкции, 8 намерений кредиторов о банкротстве, 716 активных исков на 6,76 млрд руб., 115 исполнительных производств с остатком 2,31 млн руб. - bankruptcy_risk сразу после отчёта — 1 запрос вместо 5, кэш переиспользован - resolve by=leader-name работает ToolSpec теперь либо endpoint (обёртка), либо handler (каскад) — ровно одно, это проверяется в __post_init__. Эндпоинты каскадов объявлены в HANDLER_ENDPOINTS, иначе тест покрытия методов API их не увидит. Prompts переведены на каскады: check_counterparty вызывает due_diligence_report, assess_bankruptcy_risk — bankruptcy_risk. Тесты: 193 → 272, покрытие 91 %. Добавлены test_routing.py, test_reports.py, test_cache.py. README описывает каскадную модель и сжатие ответов с замерами. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Плейбуки аудита отдаются агенту как MCP-ресурсы checko://playbooks/audit/*, поэтому ссылки на убранные get_company, get_entrepreneur и get_person инструктировали его несуществующими инструментами — тот же класс проблемы, что был в agent-guide. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Единственный её пункт — закрепление верхних границ зависимостей — уже описан в 0.2.0, а самой версии не было ни на PyPI, ни в тегах. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SDK 2.x подставляет в version пустую строку, если её не передать в конструктор Server. Отсутствие версии ничего не ломает — сервер поднимается, инструменты работают, — поэтому заметить это можно только тестом. При этом версия в server_info единственное, по чему клиент отличает сборки: 0.1.0 падала при импорте. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
В README успел попасть настоящий ИНН крупного банка, а в тесты — настоящие по виду ОГРНИП и БИК. Правило было записано в AGENTS.md, но существовало только как текст, поэтому нарушить его не стоило ничего. Настоящий идентификатор в примере — не опечатка: агент читает документацию как инструкцию и начинает обращаться к API по реальному субъекту, а в случае ИНН-12 запрашивать персональные данные конкретного человека без законного основания. Проверка ищет закавыченные последовательности из 8-15 цифр: идентификаторы всегда закавычены, а денежные суммы в примерах ответов идут числами, поэтому кавычки отделяют одно от другого без белого списка полей. Второй тест валит мёртвые записи в ALLOWED, чтобы набор сужался, а не копился. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ветка форка ставила пакет из git, потому что на PyPI лежит нерабочая 0.1.0. Для апстрима установка снова идёт штатным путём через uvx checko-mcp. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Все три невидимы на моках и в документации - вскрылись только настоящим вызовом через MCP-клиента. 1. Кэш отдавал устаревший meta.today_request_count, и счётчик суточных запросов убывал: resolve показал 51, отчёт истратил ещё 6, следующий вызов вернул 54. Агенту предписано следить по этому полю за квотой и предупреждать у 100, так что расход занижался - ошибка в опасную сторону. В ответах из кэша счётчика больше нет, вместо него meta.из_кэша. 2. ОгрДоступ=true выглядел как отсутствие данных. ФНС вправе закрыть сведения о руководителе и участниках, тогда ФИО, ОГРН и ИНН приходят пустыми. В отчёт уходило "руководитель": null - агент читает это как "руководителя нет", хотя на деле состав управления и владения проверить нельзя. Причина названа, добавлен сигнал. 3. Убытки несколько лет подряд не попадали в сигналы, хотя playbooks/audit/methodology.md этого требует - а методология отдаётся агенту как MCP-ресурс. Сервер отдавал правила, которым сам не следовал. Правило по строке 2400: убыток два и более года подряд, считая от последнего; один убыточный год - обычная волатильность. Проверено, что каждый новый тест падает без своего исправления. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Добавил коммит 1. Кэш отдавал устаревший 2. 3. Убытки несколько лет подряд не попадали в сигналы. Для каждого исправления проверено, что новый тест падает без него. Тестов 282 → 291, Отдельно отмечу, чем это полезно за пределами трёх правок: первые два дефекта внесены изменениями из этого же PR (кэш и каскадные отчёты — новый код), и ни один из них не поймали бы ни юнит-тесты, ни сверка с документацией. Живой вызов остаётся обязательным шагом приёмки. |
Что изменилось
Опубликованный пакет
checko-mcp0.1.0 сейчас не запускается вообще. Зависимостьmcp>=1.0без верхней границы резолвится в mcp 2.0.0 (вышла 28.07.2026, мажорная переработка SDK), и сервер падает при импорте:Пока чинил это, сверил все запросы с живым API 2.4 и нашёл ещё 10 расхождений, из которых часть работала молча: запрос уходил, приходил
200, а данные были не те, что просил агент.PR закрывает всё найденное, переводит проект на mcp 2.x и добавляет каскадный слой инструментов. Все исправления проверены обращением к реальному API, не только по документации.
1. Пакет не запускался
У всех зависимостей появились верхние границы, код переведён на mcp 2.x (обработчики в конструкторе
Server, явная сборка результатов, типы вmcp_typesсо snake_case-полями). HTTP-клиент — наhttpx2:httpx0.28.1 от 12.2024 больше не развивается, аmcp2.x сам зависит отhttpx2, так что иначе в дереве было бы два HTTP-стека.В CI добавлен шаг настоящего MCP-хендшейка. Именно его отсутствие позволило мажорному апгрейду SDK пройти проверки незамеченным: unit-тесты реестров проходят и тогда, когда сервер вообще не поднимается.
2. Запросы не соответствовали API
/entrepreneurполучалogrnipogrn. Поиск ИП по ОГРНИП не работал: живой API отдаётHTTP 400/searchобъявлялdate_fromиdate_tolegal-cases,contracts,inspections,timeline,fedresurs,bankruptcy-messagesAPI их принимает. ИП и физлица были отрезаны от половины инструментов/enforcementsотсутствовалby=name|okved,obj=orgfounder-name,leader-name,reg-date,upd-date,obj=ent. Поиск по ФИО учредителя и руководителя — способ найти все компании человека, не зная его ИНН/contractsбезlaw=94иsort/legal-casesописан какСписДел[]сНомерДела, фактическиЗаписи[]сНомер,Дата,Ист,Ответ. Аналогично/enforcementsmeta.balanceравен 0 постоянно, и 100 запросов при этом работают. Ориентир —meta.today_request_countПлюс:
/companyполучилokpo,/inspections—sort; сетевые сбои больше не дают пустой текст ошибки; сервер сообщает клиенту свою версию (SDK по умолчанию подставляет пустую строку).Чтобы это не разъезжалось снова,
tests/test_api_contract.pyдержит таблицу документированных параметров каждого метода и проверяет: схема инструмента не объявляет ничего, чего у метода нет, все документированные методы достижимы, а в запрос уходит ровно то, что ожидается.3. Ответы не помещались в контекст
Замеры на живом API: карточка крупной организации — 113 081 символ, расширенная финансовая отчётность — 151 597, выдача поиска — 108 191. Claude Code обрезает вывод инструмента на 25 000 токенов и делает это молча — ассистент получает оборванный JSON, не заметив обрыва.
Добавлен параметр
detail(compactпо умолчанию,fullпо запросу). Свёртывание универсальное — по длине списков, а не по белому списку полей: белый список молча терял бы новые поля API, а скалярные поля несут факторы риска. Рядом указывается, сколько элементов было всего.Те же ответы в
compact— 4 373–7 053 токена.4. Каскадная модель инструментов
13 тонких обёрток заставляли агента самому знать модель данных Checko: какой метод вызвать по числу цифр в ИНН, в каком порядке опросить шесть реестров, что не забыть про исполнительные производства. Число инструментов осталось тем же, но они выстроены в три уровня:
Уровень 1 —
resolve(наименование, ФИО или любой идентификатор → короткий список кандидатов) иprofile(карточка с автоопределением вида субъекта по числу цифр: 8 ОКПО, 9 БИК, 10 ИНН юрлица, 12 ИНН физлица, 13 ОГРН, 15 ОГРНИП; для 12 цифр сначала ЕГРИП, и только при отсутствии ИП — физлицо, о чём сказано в ответе).Уровень 2 —
due_diligence_report(шесть реестров параллельно, 5–6 запросов) иbankruptcy_risk(четыре источника, 4–5 запросов).Уровень 3 — девять обёрток над отдельными методами для детализации.
Заменяют
search,get_company,get_entrepreneur,get_person— без потери возможностей.Отчёты считают сигналы по формальным правилам, с уровнем и источником каждого, но вердикт о сделке не выносят: правила проверяемы, суждение — нет. Это закреплено тестом.
Проверка крупной организации на живом API — 6 запросов, 4 295 символов (~1 431 токен) на всё:
5. Кэш ответов
TTL-кэш в
CheckoClient.get()(CHECKO_CACHE_TTL, по умолчанию 900 с). Повторный отчёт по тому же субъекту стоит 1 запрос вместо 5 — при бесплатном тарифе в 100 запросов в сутки это существенно. Ошибки не кэшируются. Плюс повторы на429и5xxс учётомRetry-After.Валидация аргументов перенесена до создания клиента: раньше без API-ключа любая ошибка в аргументах маскировалась сообщением про ключ, а невалидный вызов расходовал платную квоту. Это проверяется тестом.
6. Тесты: 83 → 282
Покрытие 75 % → 91 %,
server.py— с 0 % до 94 %. Появилисьtest_server.py(настоящая MCP-сессия в памяти черезcreate_client_server_memory_streams),test_api_contract.py,test_routing.py,test_shape.py,test_reports.py,test_cache.py,test_repo_hygiene.py.Сервер собирается фабрикой
build_server(client_factory=None), а не на уровне модуля — именно это сделало MCP-слой тестируемым.test_repo_hygiene.pyпроверяет правило «только синтетические идентификаторы» изAGENTS.md. Оно существовало только как текст, и я сам, работая над этим PR, успел вписать в пример настоящий ИНН крупного банка. Настоящий идентификатор в документации — не опечатка: агент читает её как инструкцию и начинает обращаться к API по реальному субъекту, а в случае ИНН-12 — запрашивать персональные данные конкретного человека без законного основания.7. Беларусь
У портала Checko есть данные по организациям Беларуси (ЕГР Минюста РБ, идентификатор УНП), но в API 2.4 нет ни одного метода для них: все методы работают по ОГРН, ИНН, ОКПО и БИК. Проверял — на УНП API отвечает
200с пустымdata, а не ошибкой. Это хуже явного отказа: выглядит как «не найдено». Теперь сказано прямо в описаниях инструментов, в agent-guide и в README, аprofileотдаёт примечание.Тип изменения
Breaking: удалены
search,get_company,get_entrepreneur,get_person; минимальная версия Python поднята до 3.11 (3.10 выходит из поддержки 31.10.2026). Поэтому версия в PR —0.2.0. Если удобнее самому нарезать релиз, перенесите секцию[0.2.0]изCHANGELOG.mdв[Unreleased]и откатите версию вpyproject.toml— на код это не влияет.Чеклист
ruff check src/ tests/локально — без ошибокpytestлокально — 282 теста проходятCHANGELOG.mddocs/api/,docs/instructions/agent-guide.md,README.md,AGENTS.mdи плейбуки вplaybooks/audit/(последние отдаются как MCP-ресурсы, поэтому устаревшие имена инструментов в них активно дезинформировали агента)Что стоит знать перед мерджем
После мерджа нужен релиз на PyPI. Сейчас там 0.1.0, которая не запускается, и
uvx checko-mcpставит именно её. README в этом PR описывает установку штатным путём черезuvx checko-mcp— она заработает после публикации 0.2.0.Ветку CI стоит посмотреть отдельно: матрица расширена до Python 3.11–3.14, добавлен шаг MCP-хендшейка (
scripts/smoke.py --no-apiс фиктивным ключом, без обращений к сети).Готов разбить PR на части, если так удобнее ревьюить: исправления запросов, миграция SDK и каскадный слой независимы друг от друга.