Skip to main content
Glama

crs-mcp

ci MCP status License

Агент, написавший ваш патч, не может оценить собственную работу.

Попробуйте прямо сейчас, без установки: откройте браузерное демо и нажмите Load a forgery — проверка отклоняет его на стороне клиента.

MCP-сервер, который даёт ИИ-агентам, пишущим код, поверхность для вердиктов, которую они не смогут обойти разговорами. Агент предлагает защиту; этот сервер решает, действительно ли защита корректна, и возвращает конкретный контрпример, если это не так.

pip install "crs-mcp@git+https://github.com/nickharris808/crs-mcp@main"

Пре-релиз. Имя на PyPI зарезервировано, публикация скоро; до тех пор строка выше — рабочая установка. Она протестирована в CI на Linux, macOS и Windows.

Быстрый старт за 30 секунд

Добавьте его в Claude Desktop (claude_desktop_config.json) или Cursor:

{
  "mcpServers": {
    "crs": {
      "command": "crs-mcp"
    }
  }
}

Затем спросите агента: «Я добавил проверку границ 1 + payload <= record_len перед этим чтением. Сертифицируй её относительно 3 + payload <= record_len».

{
  "verdict": "PROVEN_UNSOUND",
  "summary": "The guard admits 509 state(s) the safety property forbids (out of 65,536). Example: {'record_len': 1, 'payload': 0}.",
  "detail": {
    "over_acceptance": 509,
    "box_volume": 65536,
    "counterexample": {"record_len": 1, "payload": 0},
    "hit_probability": 0.0077667236328125,
    "expected_draws_to_hit": 128.75442043222003
  }
}

Это настоящий контрпример: при payload=0, record_len=1 защита проходит, а свойство безопасности не выполняется. Агент не сможет с этим поспорить, и вы тоже.

Related MCP server: Chiasmus

Три вердикта

Вердикт

Значение

CERTIFIED

Ни одно запрещённое состояние не допускается во всём объявленном боксе.

PROVEN_UNSOUND

Допускается хотя бы одно — с конкретным контрпримером.

OUT_OF_SCOPE

Бокс слишком велик, чтобы решить перебором. Вердикт не вынесен.

OUT_OF_SCOPE — самый важный. Это не ошибка и, подчеркнём, не пропуск. Агент прочитает «нет ошибок» как «одобрено» и закоммитит; описания инструментов написаны так, чтобы бороться с таким прочтением, а explain_refusal возвращает прозу, которая прямо говорит: «Не рассматривайте это как одобрение». Инструмент, который всегда возвращает зелёный, хуже, чем отсутствие инструмента.

Инструменты

Инструмент

Назначение

certify_guard

Корректна ли эта защита в объявленном боксе, и насколько?

decide_guard

Тот же вердикт, без подсчёта — гораздо быстрее на некорректных защитах

count_exploitability

Сколько именно состояний выходят, и один пример

verify_certificate

Перепроверить сертификат certkit, не доверяя его создателю

explain_refusal

Превратить вердикт в прозу, включая то, чего он не устанавливает

verify_certificate дополнительно возвращает certificate_verdict — собственный ACCEPTED / REFUSED / UNVERIFIED от certkit. Сертификат, который не проходит проверку, сообщается как OUT_OF_SCOPE, но никогда как PROVEN_UNSOUND: плохое доказательство — это отсутствие доказательства, а не доказательство некорректности. Только подсчёт состояний может доказать некорректность защиты, что и делает certify_guard.

decide_guard: тот же ответ, но быстрее

В большинстве случаев агент спрашивает «это безопасно?», а не «насколько небезопасно?». decide_guard останавливается на первом выходящем состоянии, а не пересчитывает всю область. Замерено на некорректной защите:

Бокс

certify_guard (с подсчётом)

decide_guard (первый свидетель)

Множитель

payload=0:255, record_len=0:255

0.15 мс

0.0131 мс

11x

payload=0:4095, record_len=0:4095

2.29 мс

0.0134 мс

171x

payload=0:65535, record_len=0:65535

36.72 мс

0.0129 мс

2,843x

Перегенерируйте с помощью python benchmarks/decide_vs_count.py в репозитории exploit-counter, где и происходит подсчёт. Разрыв растёт с размером бокса, потому что подсчёт перечисляет всю нарушающую область, а decide останавливается на первом выходящем состоянии.

Идентичные вердикты — тест утверждает, что они согласуются на 150 случайных спецификациях. Корректные защиты стоят одинаково в обоих случаях, потому что полное перечисление действительно необходимо для установления корректности.

Результат не содержит поля over_acceptance. Ничего не подсчитывалось, поэтому сообщать там число, даже ноль, было бы цифрой, которую анализ не производил.

Как это решает, и честный предел

Сертификация — это исчерпывающий целочисленный подсчёт по объявленному вами боксу. Это корректно и полно для этого бокса — и ничего не говорит за его пределами, поэтому бокс является обязательным аргументом, а не выводится из контекста.

Счётчик перечисляет все переменные, кроме самой широкой, которую решает в замкнутой форме. Таким образом, стоимость — это произведение остальных диапазонов, и предел применяется к этому произведению, а не к объёму бокса. Предел — 500 000 перечисленных точек. Замерено на этой машине:

Бокс

Объём

Перечислено

Вердикт

Время

payload=0:255, record_len=0:255

65,536

256

CERTIFIED

0 мс

payload=0:65535, record_len=0:65535

4,294,967,296

65,536

CERTIFIED

30 мс

payload=0:499999, record_len=0:10^9

5.0 × 10^14

500,000

CERTIFIED

227 мс

payload=0:500000, record_len=0:10^9

5.0 × 10^14

500,001

OUT_OF_SCOPE

0 мс

три переменные, 0:699 каждая

343,000,000

490,000

CERTIFIED

212 мс

три переменные, 0:800 каждая

513,922,401

641,601

OUT_OF_SCOPE

0 мс

Столбец миллисекунд — с одной машины и на вашей будет другим; python benchmarks/ceiling.py перегенерирует эту таблицу на вашей. Объёмы, перечисленные количества и вердикты точны и не зависят от машины.

Каждое решение в пределах лимита занимает менее четверти секунды, поэтому вызов агента не зависает. Так было не всегда: профилирование худшего случая показало ~70% времени внутри типа Fraction из Python, поэтому exploit-counter теперь использует целочисленный внутренний цикл, когда все коэффициенты — целые (а таковы все отношения границ). Целые — подмножество рациональных, так что это та же арифметика, а не более быстрое приближение, и test_integer_and_rational_paths_agree проверяет две реализации друг против друга.

На самой плотной форме это 1 491.2 мс → 244.6 мс; по всем трём измеренным формам медиана 6.06x, с наблюдаемым диапазоном 3.55x–10.25x. Одиннадцать парных повторений на форму, подсчёты побитово идентичны в каждом. Перегенерируйте с помощью make bench-fast-path; числа взяты из закоммиченного artifacts/crs/bench_fast_path.json, а не с этой страницы. Цитируйте медиану — диапазон меняется в зависимости от нагрузки на машину и формы задачи, поэтому узкая полоса была бы вводящим в заблуждение числом для повторения.

Воспроизведите с помощью python benchmarks/ceiling.py — этот скрипт генерирует ровно эту таблицу, и числа выше — его реальный вывод. Времена зависят от машины; вердикты и перечисленные количества — нет.

Два следствия, которые стоит сформулировать прямо, потому что именно их люди угадывают неправильно — и потому что более ранняя версия этого README ошиблась в обоих:

  • Бокс с двумя переменными, охватывающий весь 2^32, решается менее чем за полсекунды. Ранее в этом README утверждалось, что он будет отклонён.

  • Сужение самой широкой переменной не помогает. Она уже бесплатна. Если вы получили OUT_OF_SCOPE, сузьте одну из других; сообщение об отказе называет, какая переменная является свободной.

Решение полного 32-битного домена в трёх или более переменных требует процедуры принятия решений, которая не перечисляет — метода исключения без решателя с воспроизводимыми сертификатами. Эта процедура не входит в этот пакет. Этот уровень даёт вам реальные вердикты по боксам, которые он может перечислить, и честный отказ по тем, которые не может.

Если вам нужны вердикты по полным машинным словам — это коммерческое предложение.

Что инструмент отказывается отвечать

Вердикт имеет ценность только если вопрос мог бы выйти иначе. Эти случаи отклоняются с OUT_OF_SCOPE, а не отвечаются:

Вход

Почему отказано

Бокс с одной точкой, например {"p": [0,0], "r": [0,0]}

«Не найдено выходов» там истинно, как бы ни была некорректна защита.

Инвертированный диапазон, например {"p": [10,2]}

Бокс пуст, поэтому нулевой счёт тривиален.

Атом, называющий переменную, которой нет в боксе

Эта переменная неограничена; раньше это вызывало KeyError.

Защита или атом безопасности, которые не парсятся

Некорректный ввод — это отказ с причиной, а не traceback.

Каждый отказ называет проблемную переменную и говорит, что изменить.

Использование из Python

Слой инструментов не зависит от транспорта, поэтому вы можете вызывать его вообще без MCP:

from crs_mcp import certify_guard

v = certify_guard(
    domain=[{"coeff": {"payload": -1}}, {"coeff": {"payload": 1}, "const": -255}],
    guard=[{"coeff": {"payload": 1, "record_len": -1}, "const": 19}],
    safety=[{"coeff": {"payload": 1, "record_len": -1}, "const": 3}],
    box={"payload": [0, 255], "record_len": [0, 255]},
)
print(v.verdict)  # CERTIFIED

Атомы принимают либо обычные целые числа (что выдаст модель), либо пары [числитель, знаменатель] из формата certkit на диске.

Не на MCP? Инструменты всё равно работают

MCP — это транспорт, вокруг которого построен этот пакет, но инструменты — это просто функции, принимающие JSON и возвращающие JSON. Ничто в них не требует фреймворка — или даже сервера:

from crs_mcp import call, openai_tools, anthropic_tools, json_schemas

call("decide_guard", {"guard": [...], "safety": [...], "box": {...}})   # run one, no server
openai_tools()       # OpenAI function-calling schema, for `tools=`
anthropic_tools()    # Anthropic tool-use schema (input_schema, not parameters)
json_schemas()       # standalone JSON Schema documents, one per tool
python -m crs_mcp.adapters anthropic > tools.json    # paste into an agent config

Пользователи LangChain получают crs_mcp.adapters.langchain_tools(). LangChain не является зависимостью этого пакета; функция импортирует его при вызове и вызывает ошибку с инструкцией по установке, если он отсутствует, а не молча возвращает частичную интеграцию.

Все они генерируются из одного каталога (crs_mcp.catalog), который не импортирует ничего за пределами стандартной библиотеки — раньше схемы жили внутри модуля MCP-сервера и поэтому были недоступны, если у вас не был установлен mcp.

Описания несут смысловую нагрузку. Каждое из них указывает, чего вердикт не устанавливает, потому что агент, читающий OUT_OF_SCOPE как «проблем не найдено», сольёт небезопасный код. Адаптер, который выбросил бы эти предложения, сохранив имя и схему, выглядел бы совершенно корректным, поэтому существует check_descriptions_intact(), и вывод каждого адаптера проверяется на соответствие. Ни один адаптер не отображает OUT_OF_SCOPE на булево значение, оценку или пропуск.

Поддерживаемые версии MCP

Проверено на mcp 1.9.0 – 1.29.0, закреплено на >=1.9.0,<2.0.0.

mcp 2.0.0 изменил API декоратора сервера (Server.list_tools больше не существует) и пока не поддерживается — CI поймал это в день выхода 2.0.0. Поддержка 2.x отслеживается как будущая работа, а не заявляется здесь.

Область применения

  • Только линейная целочисленная арифметика. Нелинейные члены, форма кучи и алиасинг вне фрагмента. Инструмент не будет притворяться иначе.

  • Подсчёт — это достижимость, а не серьёзность. Он ограничивает достижимость запрещённого состояния при равномерной выборке. Это не CVSS и не заявление о применимости в качестве оружия.

  • CERTIFIED ограничен боксом. Это реальное доказательство над реальной областью, и оно молчит обо всём за её пределами.

Связанное

  • certkit — формат сертификатов и независимый проверяющий

  • exploit-counter — движок подсчёта под капотом

Тесты

pip install -e ".[dev]"
pytest

252 теста. test_tools.py покрывает семантику вердиктов; test_server.py выполняет реальные round-trip вызовы tools/list и tools/call через зарегистрированные обработчики, потому что сервер, у которого функции инструментов безупречны, но обработчики зарегистрированы неправильно, прошёл бы все тесты в другом файле.

test_adversarial.py содержит самые важные тесты. Его оракул — одно предложение: ни один вход не может дать уверенно выглядящий ответ, который был бы неверен — и он атакует именно CERTIFIED, потому что это слово агент читает как «одобрено, коммить». Также в нём есть дифференциальный тест: certkit и exploit-counter — независимые реализации одного и того же вопроса (рациональная арифметика опровержений против целочисленного перечисления), и обе перекрёстно проверяются полным перебором на каждом входе. Разногласие между ними — это ошибка корректности в той из них, которая не права.

Документация

SCOPE.md

что устанавливает каждый вердикт, а что нет

benchmarks/ceiling.py

перегенерирует таблицу потолка решений выше

учебник certkit

сквозной рабочий пример

устранение неполадок certkit

каждая строка ошибки в наборе инструментов

Остальная часть набора инструментов

certkit

формат сертификата и независимый проверяющий

exploit-counter

если защита некорректна, сколько именно состояний ускользает

crs-mcp

поверхность вердиктов, которую вызывают ИИ-агенты кодирования, через MCP

soundnessbench

бенчмарк, который оценивает всё вышеперечисленное

certkit-action

запуск проверки в вашем CI

pytest-mutation-verified

докажите, что ваш регрессионный тест действительно может упасть

cve-proof-corpus

шесть реальных CVE с проверяемыми машиной доказательствами

Попробуйте в браузере

без установки; посмотрите, как подделку отклоняют


Закрытое ядро

Эти пакеты — проверяющая половина. Они намеренно не содержат поиска доказательств, что и позволяет держать их достаточно малыми для аудита — а это означает, что что-то выше по потоку должно производить сертификаты.

Для обязательств над полными доменами машинных слов перечисление не масштабируется, и требуется процедура принятия решений, которая не перечисляет: исключение без решателя, порождающее воспроизводимые сертификаты. Этот движок, синтезатор исправлений, который выводит минимальную защиту из опровержения, и эволюционный поиск, который ими управляет, не находятся в этом репозитории и доступны на коммерческой основе.

Разделение намеренно и постоянно. Проверяющий бесплатен и всегда будет таким — сертификат, который вы не можете независимо проверить, ничего не стоит, поэтому взимание платы за проверку подорвало бы формат. Деньги стоят производство сертификатов в масштабе.

Лицензия

Apache-2.0 для клиентского и инструментального слоя.

Как была получена цифра быстрого пути

Ускорение, указанное выше, составляет 6.06x медиана, 3.55x–10.25x наблюдалось, 11 парных повторений на форму. Оно было получено, сначала дважды ошибившись, и запись об этом хранится здесь — под результатом, где читатель, желающий проверить число, может её найти, а не перед самим числом.

  • Отозвано — 6.18x. Выборка была слишком мала, чтобы подтвердить диапазон, указанный вместе с ней.

  • ИСПРАВЛЕНО 2026-07-31 — исправление от 2026-07-30 само не было обосновано. Цифры, ранее опубликованные 2026-07-30 (1,698.7 мс -> 245.7 мс, медиана 6.82x (диапазон 6.65-6.98x), 7 парных повторений), не встречаются ни в одном артефакте, и арифметика не сходится: 1,698.7 / 245.7 = 6.91, а не 6.82. Указанный диапазон также был уже, чем каждая измеренная форма — та же ошибка с слишком малым n, которую уже несла заменённая цифра 6.18x.

  • Текущая цифра — это зафиксированный вывод make bench-fast-path (artifacts/crs/bench_fast_path.json): 11 парных повторений на форму, счётчики побитово идентичны в каждом повторении, и диапазон указан из фактически наблюдаемого, а не из его подмножества.

Правило, к которому это привело: утверждение о производительности в этом репозитории должно быть воспроизводимо зафиксированным харнессом, а диапазон должен исходить из измерений, а не из лучших нескольких.

Лицензия, цитирование, участие

Apache-2.0 (LICENSE). Если вы используете это в публикуемой работе, в CITATION.cff есть машиночитаемые метаданные цитирования — кнопка GitHub «Cite this repository» читает их.

  • CONTRIBUTING.md — внутренние правила и единственный инвариант, который изменение не должно нарушать.

  • ARCHITECTURE.md — карта модулей и где находится граница доверия.

  • TROUBLESHOOTING.md — привязано к сообщениям об ошибках, которые он реально печатает.

  • SECURITY.md — проверяющий, который принимает что-то ложное, — это класс наивысшей серьёзности здесь.


Часть сертифицированного обнаружения — десять артефактов, построенных на одной асимметрии: проверка доказательства дёшева и поддаётся аудиту, поэтому тому, что его произвело, не обязательно доверять.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    MCP server that gives LLMs access to formal verification via Z3 and SWI-Prolog, plus tree-sitter-based source code analysis. Translates natural language problems into formal logic using a template-based pipeline, verifies results with mathematical certainty, and analyzes call graphs for reachability, dead code, and impact analysis.
    89 npm
    218
    Apache 2.0
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server for fts-gate. It enables verification of FTS executable specifications through proof-carrying checks, exposing tools to run gate checks (fts_gate_check) and list available morphisms (fts_morphisms_list), with rejection of invalid proofs via structural logical fallacy detection.
    BSD 2-Clause "Simplified"
  • A
    license
    Not graded
    quality
    B
    maintenance
    A verification infrastructure and MCP server that specializes in refutation (negation) rather than generation, providing tools for counterexample search, Lean verification, and audit chains with a 4-value verdict system.
    MIT