Payment Orchestrator MCP Server
Оркестратор маршрутизации платежей
Доказательства на критическом пути, ИИ по краям.
Получив авторизацию карты, он решает, какой платежный провайдер должен её обработать — на основе эмпирических доказательств одобрения, с допуском по комиссии, который оператор задает в реальных единицах. Если попытка отклоняется, конечный автомат, ключом к которому является класс ошибки отклонения, решает, что делать дальше: тот же PSP позже, фейловер сейчас, другой канал или остановка. Само решение о маршрутизации детерминировано и аудируемо; внутри него не выполняется языковая модель.
Создано и выпущено внутри Claude Code: движок, ИИ-пограничный слой, среда оценки, веб-интерфейс, API и этот README были созданы в агентских сессиях — одна оркестрирующая сессия делегировала субагентам (бэкенд, UI, публикация, кейс) — и защищены тестами и оценками, которые вы можете запустить сами. Инженерная дисциплина та же, что описана в nutri.: конвенции, которые агент должен загрузить, структурные ограничения и машина — а не обещание — как определение готовности. Ограничения поймали то, что иначе ушло бы в продакшн: переписывание, которое теряло все пути API (найдено пост-деплойной проверкой), UI, помечавший валидных эмитентов как невиданных, и два неверных предположения автора (правило количества страниц, DNS-настройка), на которые субагенты отказались реагировать.
Живое демо https://orchestrator.vryahn.com · Кейс
https://vryahn.com/work/routing · API api/README.md ·
MCP MCP.md
Вневыборочный реплей на 84 011 ТЕСТОВЫХ транзакциях (дни 22–31, таблицы обучены на днях 1–21): ожидаемое одобрение 72,02% при cost_bias=0 против фактически наблюдаемых 66,13% — +5,89 п.п., направленно, не A/B-результат. См. Ограничения.
Запуск
Python 3.11. requirements.txt — это среда выполнения: fastapi плюс стандартная библиотека, и единственное, что устанавливает Vercel. requirements-dev.txt добавляет офлайн-стек (duckdb, pandas, numpy, pyarrow, mcp, uvicorn, httpx), необходимый для регенерации данных, запуска бэктеста, обслуживания MCP или локального тестирования.
python3.11 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt
.venv/bin/python cli.py --txn-file demo_transactions.json # 8 decision-boundary cases
.venv/bin/uvicorn api.index:app --reload --port 8000 # API on /api/*, UI from public/Таблицы, которые читает движок (routing_tables.json, routing_meta.json), закоммичены, поэтому свежий клон маршрутизирует — и деплоится — без офлайн-конвейера. Перегенерируйте их, только если модель данных изменится:
.venv/bin/python synth_attempts.py # seeded ~300k attempts -> attempts.parquet
.venv/bin/python build_routing_tables.py # -> routing_tables.json, routing_meta.json
.venv/bin/python backtest.py --json # -> backtest_summary.json
.venv/bin/python tests.py # engine
.venv/bin/python tests_ai.py # AI edges + HTTP contract
.venv/bin/python evals/decline_eval.py # normalizer against the golden setRelated MCP server: kong-ai-gateway-mcp
Архитектура
flowchart LR
subgraph offline["OFFLINE — batch, once per rebuild"]
G["synth_attempts.py<br/>seeded generator"] --> A[("attempts.parquet<br/>1 row = 1 attempt, ~300k")]
A -->|"build_routing_tables.py"| T[("routing_tables.json + routing_meta.json<br/>segment x PSP: n, approvals, p_hat, Wilson LB<br/>4-level hierarchy")]
A -->|"backtest.py — train d1-21, test d22-31"| B["backtest_summary.json<br/>out-of-sample lift"]
end
subgraph edgein["EDGE IN — language to enum"]
RAW["raw PSP decline<br/>ISO 8583 / decline_code / refusalReason / bank prose"] --> N{"decline_normalizer.py<br/>table -> LLM -> safe fallback"}
EV["evals/ — 48 golden declines<br/>accuracy by route, hallucination gate"] -.->|"scores"| N
end
subgraph online["ONLINE — pure engine, never touches raw data"]
X["txn: amount, bin6/issuer, funding,<br/>channel, attempt #, error history"] --> D{"decide(txn, config)"}
T --> D
N -->|"error_class"| D
D --> S1["1. resolve segment per PSP<br/>walk L0 to L3 until n >= min_support"]
S1 --> S2["2. score = Wilson LB x amount x (1 - fee)<br/>= expected net collected"]
S2 --> S3["3. pick PSP — cost_bias 0..1 maps to<br/>fee tolerance 0..10pp; psps_down excluded"]
S3 --> S4["4. retry state machine<br/>keyed on last error_class"]
S4 --> R["Decision: route_psp, eligible_psps with scores,<br/>retry policy, reasoning lines"]
end
R --> OPS["ops.py — route, explain, simulate,<br/>evidence, normalize, backtest"]
B --> OPS
OPS --> CLI["cli.py"]
OPS --> API["api/index.py — FastAPI on Vercel<br/>+ public/ web UI, same origin"]
OPS --> MCP["mcp_server.py — 6 MCP tools"]Почему такая архитектура
Офлайн/онлайн-разделение.
decide(txn, config) -> Decisionчистый. Он загружает предварительно материализованные таблицы один раз и никогда не читает сырые попытки, поэтому решение занимает микросекунды, тестируется без базы данных и аудируется постфактум. Это та же граница, которую вы бы провели в продакшне между слоем сверки и слоем маршрутизации.Иерархия сегментов с фолбэком. L0 — это
gateway_group × funding × issuer_bucket × amount_band; L3 — этоgateway_groupсам по себе. Поддержка разрешается для каждого PSP, по одному измерению за раз (диапазон сумм → эмитент → funding), пока ячейка не превыситmin_support(по умолчанию 200), и использованный уровень сообщается вместе с решением. Канал является первостепенным и никогда не отбрасывается: пользователь-присутствующий и офлайн-сессии — это разные миры.Нижняя граница Уилсона, а не сырая доля. Сегмент с 3/3 одобрений — это не 100%-ный сегмент. Граница сжимается к нулю по мере разрежения поддержки, поэтому хорошо обоснованные 78% побеждают удачные 100% без отдельного правила доверия, прикрученного сбоку.
cost_biasкак явная ручка в реальных единицах. Компромисс формулируется как «процентные пункты одобрения, которые я отдам за более дешевый PSP» —tolerance = cost_bias × 10 п.п., и побеждает самый дешёвый PSP в пределах этого допуска от лучшего по одобрению. Смешанная оценка позволила бы разнице в доли процента комиссии молча перевесить двузначный разрыв в одобрении; фильтр допуска — нет. Бэктест оценивает ручку: 72,02% / +5,89 п.п. одобрения приcost_bias=0, 71,56% / +5,43 п.п. при 0,5, 69,57% / +3,44 п.п. при 1,0.Повтор по классу ошибки, а не по слепому счётчику.
insufficient_funds— это проблема счёта, и он повторяет того же PSP в следующем платёжном окне;bank_auth_requiredв офлайн-сессии не может быть удовлетворён без клиента, поэтому он переносится на канал с присутствием пользователя, а не сжигает попытки;fraud_riskнавсегда останавливает цепочку;generic_declineпереключается на следующего PSP по рейтингу. Неизвестный класс деградирует до общей политики фейловера и сообщает об этом.
Где ИИ уместен — а где нет
Внутри decide() нет LLM. Деньги не должны двигаться на основе сэмплированного токена. Языковая модель ограничена двумя краями, где естественный язык действительно является проблемой.
Внутри — decline_normalizer.py. Каждый PSP отклоняет на своём диалекте: числовые коды ISO 8583, Stripe-подобный decline_code, Adyen-подобный refusalReason или сырой банковский текст. Конечный автомат повторов завязан на одно перечисление, поэтому диалекты должны схлопнуться до того, как движок их увидит. Детерминированная таблица обрабатывает коды, несущие основной объём, — уверенность 1.0, нулевая задержка, нулевая стоимость. Только промах таблицы доходит до цепочки моделей (Gemini, затем Mistral), которая отвечает по ограниченной схеме перечисления. Всё, что вне перечисления или ниже уверенности 0,6, отбрасывается в пользу generic_decline, который является безопасным значением по умолчанию для политики повторов. Репозиторий проходит зелёным без заданных API-ключей.
Измерено, а не принято на веру — evals/. 48 золотых отклонений: ~60% попаданий в таблицу, ~40% намеренно вне таблицы (опечатки, многословный банковский текст, необычные коды) плюс несколько действительно неоднозначных, где generic_decline — правильный ответ. evals/baseline.json фиксирует два базовых уровня. Только таблица (без ключей): 32/48 = 66,67%, т.е. 100% на 28 случаях с табличным маршрутом и безопасный generic_decline по умолчанию на 20, которые проваливаются. LLM (ключи настроены, запуск против развёрнутого API с --remote): 48/48 = 100% — 28 табличных, 19 отвечено gemini-3.6-flash, 1 — низкоуверенным фолбэком, где ожидаемым ответом был generic_decline. Запускатор сообщает точность по маршрутам и по классам, утверждает отсутствие галлюцинаций и валит сборку, если точность падает более чем на 2 п.п. ниже соответствующего базового уровня.
Снаружи — mcp_server.py. Шесть MCP-инструментов — route_transaction, explain_decision, simulate, segment_evidence, normalize_decline, backtest_summary — позволяют агенту управлять движком на английском. Агент может запрашивать каждое решение, но не менять ни одного. См. MCP.md.
Ограничения
Данные синтетические. Структура призвана сделать решения о маршрутизации нетривиальными, а не воспроизвести какой-либо реальный портфель.
Нет живых PSP-коннекторов: движок решает, но не отправляет.
Нет скоринга фрода, оркестрации 3DS, сетевых токенов или принудительного применения правил повторов схемы.
Бэктест носит направленный характер. Историческая маршрутизация не была рандомизирована, лимиты мощности не моделируются, а «ожидаемое одобрение» — это ставка Уилсона-НБ за период TRAIN, применённая к объёму TEST, а не живой A/B-результат.
Таблицы объединяют все попытки, в то время как бэктест обучается и воспроизводится только на первых попытках; продакшн-таблицы только для первых попыток были бы следующим исправлением.
LLM-оценка — это 48 случаев и один прогон; 100% на таком маленьком золотом наборе — это защита от регрессий, а не заявление о длинном хвосте в проде.
Карта файлов
файл | назначение |
| сидированный генератор для |
| строит |
| движок: |
| диалекты отклонений PSP → перечисление |
| операторские функции (route, explain, simulate, evidence, normalize, backtest), общие для API и MCP |
| FastAPI на Vercel; контракт в |
| MCP stdio-сервер, шесть инструментов; см. |
| CLI-фронтенд: одна транзакция через флаги или пакет через |
| статический веб-интерфейс, обслуживается Vercel с того же источника, что и API |
| 8 транзакций на границах решений с примечаниями |
| реплей TRAIN дни 1–21 / TEST дни 22–31 при |
| 48 золотых отклонений, скоринг-раннер и записанный базовый уровень |
| проверки на основе assert: движок, затем ИИ-края и HTTP-контракт |
Брайан Родригес Абарка · vryahn.com · Начато с технического упражнения, обобщено как личный проект. Синтетические данные.
This server cannot be deployed
Maintenance
Related MCP Connectors
The system of record for AI agent authority: playbooks, routed policy questions, reusable rules.
Read-only AI coding tools for change verification, release readiness, capacity, and guidance.
Machine-paid revenue intelligence, recovery decisions, and x402 routing for AI agents.
See, price, and control every tool call your AI agents make: policy checks, cost, and audit tools.
Related MCP Servers
- FlicenseNot gradedqualityBmaintenanceEnables read-only investigation of payment transactions by building normalized timelines, detecting anomalies like duplicate charges and stuck refunds, and creating diagnostic escalations for human review. Never executes or modifies payment actions.-
- FlicenseAqualityBmaintenanceEnables an LLM agent to investigate a Kong Gateway through fourteen read-only, schema-validated tools and a deterministic diagnostic engine, so it can trace routes, services, plugins and upstream health and explain faults such as why authentication is failing on a path. Every fact comes from the gateway itself, and the model only selects tools and writes the evidence-backed explanation.14-
- AlicenseAqualityBmaintenanceEnables AI agents to investigate synthetic fraud cases by pulling transactions, running deterministic rules evaluations, pivoting across devices, and recording auditable case files with provenance.4MIT

Jev MCP Serverofficial
AlicenseNot gradedqualityDmaintenanceEnables AI clients to run read-only yes/no checks, classifications, and ordered scores with probabilities, supporting triage, routing, sentiment, and lead-fit decisions through hosted or self-hosted MCP tools.41 npmAGPL 3.0