Skip to main content
Glama
vryahn

Payment Orchestrator MCP Server

by vryahn

Оркестратор маршрутизации платежей

Доказательства на критическом пути, ИИ по краям.

Получив авторизацию карты, он решает, какой платежный провайдер должен её обработать — на основе эмпирических доказательств одобрения, с допуском по комиссии, который оператор задает в реальных единицах. Если попытка отклоняется, конечный автомат, ключом к которому является класс ошибки отклонения, решает, что делать дальше: тот же 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 set

Related MCP server: ai-log-mcp-server

Архитектура

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% на таком маленьком золотом наборе — это защита от регрессий, а не заявление о длинном хвосте в проде.

Карта файлов

файл

назначение

synth_attempts.py

сидированный генератор для attempts.parquet; микс каналов, комиссии PSP, модель одобрения, микс ошибок и поведение повторов описаны вверху

build_routing_tables.py

строит routing_tables.json (сегмент × PSP: n, одобрения, p_hat, wilson_lb, все 4 уровня) и routing_meta.json (границы диапазонов сумм, белый список эмитентов, карта bin6 → эмитент, комиссии PSP, перечисление классов ошибок)

orchestrator.py

движок: decide(txn, config) -> Decision, датакласс Config. Только стандартная библиотека

decline_normalizer.py

диалекты отклонений PSP → перечисление error_class движка: сначала таблица, LLM при промахе, безопасный фолбэк

ops.py

операторские функции (route, explain, simulate, evidence, normalize, backtest), общие для API и MCP

api/index.py

FastAPI на Vercel; контракт в api/README.md

mcp_server.py

MCP stdio-сервер, шесть инструментов; см. MCP.md

cli.py

CLI-фронтенд: одна транзакция через флаги или пакет через --txn-file

public/

статический веб-интерфейс, обслуживается Vercel с того же источника, что и API

demo_transactions.json

8 транзакций на границах решений с примечаниями why_interesting

backtest.py

реплей TRAIN дни 1–21 / TEST дни 22–31 при cost_bias 0 / 0,5 / 1,0; --json перезаписывает backtest_summary.json

evals/

48 золотых отклонений, скоринг-раннер и записанный базовый уровень

tests.py / tests_ai.py

проверки на основе assert: движок, затем ИИ-края и HTTP-контракт


Брайан Родригес Абарка · vryahn.com · Начато с технического упражнения, обобщено как личный проект. Синтетические данные.

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables natural-language investigation of Datadog data including logs, metrics, monitors, traces, hosts, dashboards, events, and incidents, all through read-only API access.
    2,053
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Investigate fraud directly from Claude, Cursor, or any MCP-compatible client. Analyze suspicious activity with clear, evidence-backed verdicts. Pivot from a single signup to every account sharing the same device, IP address, or email inbox. Check entities against a cross-operator abuse network, review linked accounts, and efficiently process your fraud review queue. Read-only by default, with no r
    10
    269
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables operations teams to diagnose and resolve stuck orders via natural-language queries. It provides evidence-based resolution proposals, but any state-changing action requires explicit human confirmation.

View all related MCP servers

Related MCP Connectors

  • See, price, and control every tool call your AI agents make: policy checks, cost, and audit tools.

  • A paid remote MCP for AI SDK data query MCP, built to return verdicts, receipts, usage logs, and aud

  • Enterprise AI Control Plane: governance, guardrails, spend tracking, compliance & smart routing.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/vryahn/payment_orchestrator'

If you have feedback or need assistance with the MCP directory API, please join our Discord server