fts-gate-mcp
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.
README
fts-gate
Верификационный гейт поверх FTS — языка исполняемых спецификаций. Ключевое свойство: отказ является следствием типовой ошибки, а не решением языковой модели.
Когда обычный ассистент «не отвечает», это его выбор, который можно
переформулировать, уговорить или обойти джейлбрейком. Когда отказывает fts-gate,
система не смогла построить доказательство: для морфизма, которого нет в
библиотеке проверенных законов, частичная функция M(m) не определена, правило
типизации APPLY неприменимо, и вывода просто не существует. Уговорить нечего.
Границы гарантии описаны предельно честно в docs/guarantees.md — включая рабочий контрпример, который проходит все проверки FTS полностью зелёным и всё-таки выдаёт привилегированный доступ по одному паролю.
Архитектура
spec_source (.fts) context (JSON)
│ │
▼ │
┌──────────────────────────────────────────┐ │
│ 1. compile() → PARSE_ERROR │ │
├──────────────────────────────────────────┤ │
│ 2. validate() → TYPE_ERROR │ │
│ Σ = (C, Δ, M): имена, типы, apply │ │
├──────────────────────────────────────────┤ │
│ 2.5 детектор: структура вывода │ │
│ → CIRCULAR_PREMISE, REVERSED_MORPHISM,│ │
│ VACUOUS_MORPHISM, EQUIVOCATION, │ │
│ REIFICATION │ │
├──────────────────────────────────────────┤ │
│ 3. discharge premises │◀── morphisms/manifest.json
│ → UNVERIFIED_MORPHISM │ verified / derived / proposed
├──────────────────────────────────────────┤ │
│ 4. testUtilities() → PROPERTY_VIOLATION│ │
│ примеры и свойства │ │
├──────────────────────────────────────────┤ │
│ 4.5 детектор: слой утилит │ │
│ → NON_EXHAUSTIVE, UNDECLARED_BOUNDARY,│ │
│ EXAMPLE_AS_PROOF │ │
├──────────────────────────────────────────┤ │
│ 5. certify() (производитель) │◀─────┤
│ → CERTIFICATE_ERROR │ │
├──────────────────────────────────────────┤ │
│ 6. verify() (независимый проверяющий) │◀─────┘
│ → VERIFICATION_ERROR │
└──────────────────────────────────────────┘
│
┌────────────┴────────────┐
▼ ▼
status: "certified" status: "refused"
result, certificate, code, detail,
morphisms_used, digests missing_morphisms?, failures?,
fallacies?, explains?, verdict?
Детектор разбит на два прохода не для красоты: дефекты самого вывода (круг, обратное направление, пустой домен) не зависят от того, допустимы ли посылки, и сообщаются до обращения к манифесту; дефекты слоя утилит сообщаются после исполнения примеров, чтобы сломанный пример оставался сломанным примером, а не превращался в претензию к обобщению.
Шаги 5 и 6 разделены намеренно: производитель доказательства и проверяющий не делят состояние, как того требует proof-carrying подход. Проверяющий заново строит вывод из документа и контекста и сравнивает дайджесты.
ftsGate никогда не бросает исключение наружу — любое непредвиденное условие
становится отказом INTERNAL_ERROR, поэтому вызывающий всегда может ветвиться по
полю status.
Компоненты
| Путь | Назначение |
|---|---|
src/gate.ts |
ftsGate(spec_source, context, options) -> GateResult |
src/fallacies.ts |
детектор структурных логических ошибок: detectFallacies(document, options) -> FallacyFinding[] |
src/manifest.ts |
загрузка и валидация библиотеки морфизмов, проверка композиции derived |
src/cli.ts |
fts-gate check / fts-gate morphisms |
src/mcp.ts |
MCP-сервер: fts_gate_check, fts_morphisms_list |
morphisms/ |
.fts-модули доменных аксиом + manifest.json |
grammars/fts.gbnf |
GBNF-грамматика для constrained decoding |
examples/ |
успешные пути, контрпример, фикстуры каждого отказа |
examples/fallacies/ |
по фикстуре на каждый код детектора, с разбором ошибки в шапке файла |
scripts/scan-fallacies.mjs |
npm run fallacies:scan — прогон детектора по всему корпусу .fts |
Сам язык FTS — отдельный пакет @digitable/fts;
гейт подключает его как обычную зависимость и ничего из него не копирует.
Коды отказа
| Код | Что не удалось | Дополнительные поля |
|---|---|---|
PARSE_ERROR |
разобрать поверхностный синтаксис | diagnostics |
TYPE_ERROR |
проверить сигнатуру: имена, типы полей, операнды, типизация apply/compose |
diagnostics |
UNVERIFIED_MORPHISM |
снять предпосылку: морфизма нет в манифесте, он proposed, либо его сигнатура подменена |
missing_morphisms |
PROPERTY_VIOLATION |
исполнить примеры без нарушений: расхождение с ожидается или нарушение блока свойство |
failures |
CERTIFICATE_ERROR |
построить сертификат: witness не разрешился по пути или не совпал со значением (а также нехватка свидетельства при requireEvidence) |
diagnostics |
VERIFICATION_ERROR |
подтвердить сертификат независимой перепроверкой | — |
MANIFEST_ERROR |
загрузить библиотеку морфизмов: нет source у verified, derived не композируется, дубли, опора на proposed |
— |
INTERNAL_ERROR |
что-либо непредвиденное; гарантирует отсутствие исключений наружу | — |
Причины UNVERIFIED_MORPHISM различаются полем reason: not_in_manifest,
proposed, signature_mismatch.
Коды детектора логических ошибок
src/fallacies.ts превращает каталог логических ошибок (67 позиций) в
механические проверки над Σ = (C, Δ, M).
Каждый отказ несёт fallacies (список находок), explains (глава курса,
объясняющая ошибку) и verdict.
| Код | Что не удалось | Каталог | Глава |
|---|---|---|---|
CIRCULAR_PREMISE |
получить независимое основание: петля dom(m) = cod(m), цепочка вывода, вернувшаяся к своему начальному типу, или цикл в derived_from |
№10 | 08, «Проверка графом оснований» |
NON_EXHAUSTIVE |
покрыть разбираемое поле: непокрытая полярность признака или ограниченный промежуток между порогами |
№8 | 08, §6 |
EQUIVOCATION |
удержать один смысл за идентификатором: поле с двумя типами, закон с двумя сигнатурами | №23, №31 | 08, §15 |
REVERSED_MORPHISM |
применить морфизм в объявленную сторону: P → Q, Q ⊢ P или обращение закона библиотеки |
№15, №33 | 06, §1 и §3 |
EXAMPLE_AS_PROOF |
опереть свойство на примеры, задевающие все ветки правил |
№2 | 09 |
VACUOUS_MORPHISM |
найти населённый домен: witness такого типа построить нечем | №66 | 06, §13 |
UNDECLARED_BOUNDARY |
определить поведение ровно на пороге: два правила срабатывают в одной точке | №30 | 08, §7 |
REIFICATION |
различить вещь и высказывание о вещи: объект несёт сам себя как состояние | №49 | 08, §20 |
Отказ детектора означает отказ в сертификации, а не ложность содержания
(каталог №67, «Ошибка ошибки»). Это не риторика, а инвариант: поле verdict
проговаривает границу дословно, а тест механически запрещает словам «ложно» и
«неверно» появляться в тексте отказа.
Границы детектора — сколько позиций каталога он покрывает, почему «ошибок не найдено» не значит «ошибок нет» и почему устранение структурных дефектов делает ложный вывод убедительнее, — в docs/guarantees.md §2.4.
Отключается опцией detectFallacies: false: гейт ведёт себя ровно как до
появления детектора. Полезно для ablation и для замера «сколько добавил
детектор».
Библиотека морфизмов
morphisms/manifest.json — единственный источник истины о том, какие доменные
законы допустимы как предпосылки. В этом репозитории: 10 записей — 4 морфизма
модуля access-control.fts (3 verified, 1 derived), 1 отклонённый закон
контрпримера (proposed) и 5 примитивов стандартной библиотеки FTS.
| Уровень доверия | Смысл | Допустим как предпосылка |
|---|---|---|
verified |
проверено человеком, обязательна ссылка source на документ ревью или внешний источник |
да |
derived |
выведено из verified типизированной композицией; цепочка проверяется механически |
да |
proposed |
предложено моделью, не проверено (или проверено и отклонено) | нет |
Морфизм сопоставляется с записью по имени, после чего сверяется вся сигнатура: домен, кодомен и идентификатор закона. Поэтому нельзя одолжить доверие у чужого имени, подставив под него другой домен.
Записи derived не принимаются на слово: при загрузке манифеста проверяется, что
цепочка действительно композируется (dom(m₁) = dom(m), cod(mᵢ) = dom(mᵢ₊₁),
cod(mₙ) = cod(m)) и не опирается на proposed.
Каждый verified-морфизм несёт поле source со ссылкой на конкретный документ,
по которому закон был проверен человеком. Для опубликованного набора это
docs/morphism-review.md: по разделу на закон, с тем,
что именно проверено и чего закон не утверждает.
Что лежит в этом репозитории, а что нет
Рабочий набор доменных морфизмов Digitable в этот репозиторий не входит.
.fts-модули с реальными правилами живут в закрытом репозитории и подключаются к гейту черезmanifestPath/manifestили через собственный каталогmorphisms/.Здесь опубликованы только схема библиотеки — формат
manifest.json, его валидация вsrc/manifest.tsи проверка композицииderived— и один нейтральный пример модуля,morphisms/access-control.fts(MFA и привилегированный доступ), чтобы формат было на чём показать.
Добавление морфизма
- Объявить его в
.fts-модуле вmorphisms/вместе спо закону «id». - Добавить запись в
manifest.jsonс той же сигнатурой и полемsource. npm test— тестeach .fts module declares exactly the morphisms the manifest attributes to itне даст манифесту и модулю разойтись для тех модулей, которые лежат рядом.
Подключение
Требования
Node.js ≥ 20 (проверено на v24.18). Единственная рантайм-зависимость — сам язык
FTS, пакет @digitable/fts.
Пакет пока не опубликован в npm, поэтому зависимость объявлена git-адресом. То,
что npm упаковывает из того репозитория, не содержит dist/ (это артефакт
сборки, а prepare-скрипта там нет), поэтому установленную зависимость нужно
один раз собрать. Это делает scripts/bootstrap-fts.mjs, подключённый как
prepare: он скачивает src/**/*.ts ровно того коммита, который поставил npm,
и компилирует его локальным TypeScript. Отдельно скрипт вызывается как
npm run bootstrap:fts; он идемпотентен и станет лишним, как только
@digitable/fts появится в npm.
Сборка
npm ci # ставит зависимости и собирает dist FTS (prepare)
npm test # 62/62
npm run grammar:check # 26 / 10 / 200
Библиотека
import { ftsGate } from "@digitable-lol/fts-gate"
const result = ftsGate(source, context)
if (result.status === "refused") {
console.error(result.code, result.detail, result.missing_morphisms)
} else {
console.log(result.certificate.certificate_digest, result.morphisms_used)
}
Опции: requireEvidence (отклонять symbolic и trivial сертификаты),
manifestPath / manifest (альтернативная библиотека морфизмов).
CLI
node dist/src/cli.js check examples/access-revocation.fts \
--context examples/access-revocation.context.json --pretty # exit 0
node dist/src/cli.js check examples/password-mfa.fts \
--context examples/password-mfa.context.json --pretty # exit 1
node dist/src/cli.js morphisms --trust proposed --pretty
Коды возврата: 0 — сертифицировано, 1 — отказ (код в поле .code), 2 —
ошибка вызова или ввода-вывода. В stdout всегда ровно один JSON-объект.
MCP
{
"mcpServers": {
"fts-gate": {
"command": "node",
"args": ["/абсолютный/путь/до/fts-gate/dist/src/mcp.js"]
}
}
}
Два read-only инструмента:
fts_gate_check—{ source, context?, require_evidence? }→ полныйGateResult. Отказ возвращается как нормальный результат (isError: false): гейт отработал штатно.isError: trueозначает сломанный вызов или сломанную библиотеку, а не отказ.fts_morphisms_list—{ trust?, domain? }→ библиотека морфизмов с уровнями доверия, источниками и дайджестом манифеста.
Грамматика для constrained decoding
grammars/fts.gbnf — GBNF для llama.cpp, покрывающая русскую отступную
поверхность целиком: категория, объект/структура, морфизм, утилита
(правило, свойство, пример), теорема, кавычки-ёлочки, все фразы сравнения,
процентные операнды, комментарии //.
Это строгое подмножество языка парсера. Сознательные ограничения: отступ ровно два пробела, только русская поверхность, фиксированный порядок строк утилиты, одна теорема в конце документа, без одиночных кавычек и блочных комментариев. Полный список — grammars/README.md.
Проверяется с двух сторон командой npm run grammar:check (собственный парсер
GBNF и распознаватель, llama.cpp не нужен):
positive corpus — 26 accepted (все .fts русской поверхности репозитория и примеры пакета @digitable/fts)
negative corpus — 10 rejected
generated corpus — 200 строк сэмплированы из грамматики и поданы в compile(): 0 синтаксических отказов
Грамматика гарантирует синтаксис и ничего больше: сгенерированный документ всё
ещё может не пройти validate и тем более может быть содержательно ложным.
Детектор как функция награды (задел на обучение генератора)
Детектор даёт бесплатный автоматический negative reward: спецификация с кругом в основаниях, обратным морфизмом или непокрытой веткой отвергается кодом, детерминированно, без разметки и без модели-судьи. Это готовый источник сигнала для обучения генератора спек (например, через GRPO), и ниже описан интерфейс, а не реализация обучения.
import { compile } from "@digitable/fts"
import { detectFallacies, ftsGate, loadManifest } from "@digitable-lol/fts-gate"
const manifest = loadManifest() // грузится один раз, кэшируется
// Уровень 1. Только структура: не нужен ни контекст, ни библиотека морфизмов.
export function structuralPenalty(sample: string): number {
let document
try {
document = compile(sample)
} catch {
return -1 // синтаксис: сигнал есть и без детектора
}
return -detectFallacies(document, { manifest }).length
}
// Уровень 2. Весь гейт: одно число и код, по которому видно, что именно сломано.
export function gateReward(sample: string, context?: unknown): { reward: number; code?: string } {
const result = ftsGate(sample, context)
if (result.status === "certified") return { reward: 1 }
return { reward: -1, code: result.code }
}
Свойства, из-за которых это работает как награда:
- разметка не нужна. Метка вычисляется из самого сэмпла;
- детерминированность. Один и тот же вход даёт один и тот же список находок в одном и том же порядке — награда воспроизводима между эпохами;
- дёшево. Проверки — проходы по разобранному документу; тяжёлых шагов нет;
- градуированность.
detectFallaciesвозвращает список, а не булево: можно штрафовать по числу находок и по коду отдельно, аfinding.explainsдаёт текстовое объяснение для reward-модели или для датасета исправлений; - разделение уровней.
detectDerivationFallaciesиdetectUtilityFallaciesвызываются по отдельности, если нужно наградить только один слой.
И два предупреждения, без которых интерфейс легко применить не туда:
- Награда обучает форме, а не истине. Генератор, максимизирующий этот
сигнал, научится писать структурно безупречные спецификации — в том числе
безупречно ложные.
examples/password-mfa.ftsнабирает максимум по детектору. Единственный источник предметной истинности — ревью морфизмов в манифесте, и в награде он присутствует только какUNVERIFIED_MORPHISM, то есть как «взял закон из проверенного списка», а не «сказал правду». - Отсутствие находок — не подтверждение. Нулевой штраф означает «известные проверки промолчали», а не «ошибок нет» (docs/guarantees.md §2.4).
Тесты
npm test — 62 теста. На каждый код отказа минимум один, плюс успешные пути,
детерминированность, независимость проверяющего и контрпример.
| Тест | Проверяет |
|---|---|
PARSE_ERROR |
незакрытая кавычка-ёлочка |
TYPE_ERROR |
синтаксически безупречное правило со ссылкой на необъявленное поле |
UNVERIFIED_MORPHISM / not_in_manifest |
корректно типизированный морфизм, которого нет в библиотеке |
UNVERIFIED_MORPHISM / signature_mismatch |
подмена домена под проверенным именем |
UNVERIFIED_MORPHISM / proposed |
контрпример: пароль засчитан как второй фактор |
PROPERTY_VIOLATION / property_violated |
правила на 15 % + 10 % против свойства «не больше 20 %» |
PROPERTY_VIOLATION / example_mismatch |
пример ожидает 3000, правила дают 2000 |
CERTIFICATE_ERROR |
requireEvidence против trivial и symbolic сертификатов |
MANIFEST_ERROR |
verified-запись без source |
| успешный путь | apply с одним морфизмом; compose с двумя; дайджесты; assumptions |
| независимость проверяющего | подделанный и переподписанный сертификат отвергается |
| устойчивость | ftsGate не бросает исключение ни на одном мусорном входе |
CIRCULAR_PREMISE |
петля dom = cod; цепочка A → B → C → A; цикл в derived_from |
REVERSED_MORPHISM |
witness со стороны кодомена; обратное направление закона библиотеки |
VACUOUS_MORPHISM |
морфизм с ненаселённым доменом при объявленной теореме |
EQUIVOCATION |
поле «сумма» как деньги и как строка; один закон с двумя сигнатурами |
REIFICATION |
объект, несущий сам себя как состояние |
NON_EXHAUSTIVE |
промежуток (50000, 100000) между порогами; непокрытая полярность признака |
UNDECLARED_BOUNDARY |
два правила, срабатывающие ровно при сумме 1000 |
EXAMPLE_AS_PROOF |
свойство при правиле, не задетом ни одним примером |
| негативы детектора | по одному «промаху мимо дефекта» на каждый код: смежные пороги, одиночное правило поверх умолчания, широкое перекрытие, одинаковые типы поля, носитель с отдельным именем |
| весь корпус чист | ни одна проверенная спека репозитория не даёт находки |
| №67 «Ошибка ошибки» | ни один отказ не содержит слов «ложно», «неверно» и синонимов; verdict на месте |
| канарейка | вендорный парсер по-прежнему выдаёт диагностику, из которой читается обратное применение |
Лицензия
BSD 2-Clause — как и сам язык FTS, от которого проект наследует. Происхождение
кода и материалов описано в NOTICE.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.