MCP Gateway
MCP Gateway — Этап 1 (KLIP, только чтение)
Единый защищённый сервис, который позволяет авторизованным сотрудникам Energi-Up запрашивать KLIP на естественном языке через Claude, не открывая приложение. Строго только чтение.
Целевое развёртывание (PRD Q3, теперь закрыто): <gateway-hostname> -> <gateway-public-ip>
(ECS-MCP, ap-southeast-5). Обратите внимание: имя хоста — mcp-gw, а не mcp.example.com,
как предполагалось в документах v0.9; PRD/TSD следует обновить соответствующим образом.
Документы: PRD v0.9 · TSD v0.9 · Руководство по внедрению · Ревью дизайна · Runbook развёртывания
1. Зафиксированные версии (T-1)
Ревision спецификации MCP и версия SDK зафиксированы здесь. Если собственная документация SDK расходится с проектными документами, решающим является SDK — он определяет точный API.
Компонент | Зафиксировано | Примечания |
| 1.30.0 (точно, без каретки) | Опубликован 27 июля 2026 г. Предоставляет OAuth AS, транспорт Streamable HTTP и |
Ревision протокола MCP | 2025-11-25 реализован; формат провода совместим с клиентами | Ревision |
Node.js | 22 LTS ( | |
TypeScript | 5.9.3, | |
express | 5.2.1 | Повышено с 4.x из TSD: SDK зависит от |
zod | 4.4.3 | Повышено с 3.x из TSD: определения типов SDK нацелены на zod 4. Обратите внимание: |
jose | 6.2.9 | Токены шлюза RS256. |
| 2.1.0 | Замена для |
axios | 1.19.0 | Клиент KLIP, за защитой метода. |
pg | 8.23.0 | |
PostgreSQL | 16 (контейнер) | |
nginx | mainline от nginx.org | Конфигурация в |
Related MCP server: Snowflake MCP Server
2. Структура
src/
core/ config, logger, db, audit, cache, rateLimit, semaphore, migrate, errors
adapters/klip/ routes(APPENDIX A) · fields(APPENDIX A) · client(guard) · session · paginate · normalize
tools/klip/ 9 tool definitions + shared parameter plumbing
mcp/ server, envelope, runner
auth/ keys, hub(OIDC RP), users, clients, tokens, provider, loginPage
http/ app, consent(Hub + break-glass), health, origin, clientIp
migrations/ idempotent SQL (001 schema, 002 Hub OIDC)
deploy/ nginx config, backup sidecar
test/ 120 tests + mock KLIP and mock Hub fixturesПравило уровней (T-3): tools → adapters → core. Ничто не импортирует http/, кроме
точки входа. Вся бизнес-нормализация находится в adapters/klip/normalize.ts, который
ничего не импортирует, поэтому его можно юнит-тестировать изолированно.
3. Аутентификация — Downstream Hub OIDC
Пилотные пользователи входят через Downstream Hub (OIDC). Шлюз остаётся
сервером авторизации, с которым общается Claude; Hub — один шаг внутри его собственного
потока /authorize.
Мы сознательно не используем ProxyOAuthServerProvider из SDK. Проксирование
передало бы Claude токен Hub, что нарушило бы привязку аудитории по RFC 8707, дало бы
Claude более широкие области Hub, чем klip:read, и вывело бы выпуск токенов из-под
нашего контроля, так что аварийный рубильник S8 больше не смог бы аннулировать активные сессии.
Claude ──/authorize──▶ gateway ──302──▶ Downstream Hub ──302──▶ /authorize/hub/callback
│ │
│ validate id_token (sig/iss/aud/nonce) │
│ check the pilot ALLOWLIST │
◀──────────────────────────────────────────────┘
└──302 code──▶ Claude ──/token──▶ gateway token (klip:read)Аутентификация — это не авторизация. Hub доказывает, кто этот человек; таблица
users решает, может ли он пользоваться коннектором. На этапе 1 используется один общий
сервисный аккаунт KLIP, поэтому каждый допущенный пользователь может читать всё, что может
прочитать MCP_READONLY (ревью H8) — членство в пилоте и есть контроль доступа к данным.
Аккаунт Hub, которого нет в списке, получает 403, а ограничение <= 15 обеспечивается
командой user:add.
В потоке выполняются два обмена PKCE; не смешивайте их. Собственный
code_challenge от Claude защищает участок Claude→шлюз (обрабатывается SDK); отдельный
верификатор, который шлюз хранит на своей стороне, защищает участок шлюз→Hub.
Какой клиент Hub? Собственный шлюза — не KLIP
Зарегистрируйте новый OIDC-клиент для MCP Gateway. Не используйте повторно регистрацию KLIP в Hub, даже если KLIP уже зарегистрирован в тестовом DWS Hub.
Hub касается только одной из двух границ доверия:
Граница | Учётные данные | Участвует ли Hub? |
Claude → шлюз (какой человек спрашивает) | собственный клиент Hub шлюза + пилотный список допуска | да |
шлюз → KLIP (чтение данных) |
| нет — PRD §7 исключает это явно |
Повторное использование клиента KLIP конкретным образом ломает первую границу. Шлюз
проверяет aud ID-токена по своему собственному HUB_CLIENT_ID; совместное использование
идентификатора клиента KLIP означает, что ID-токен, выпущенный при входе в KLIP, был бы
принят шлюзом — это форма confused-deputy. Отдельные клиенты — это то, что делает две
полагающиеся стороны различимыми. Это также сохраняет независимость списков разрешённых
redirect-URI, секретов клиентов, графиков ротации, записей аудита Hub SSO и переключателей
отключения — отключение клиента Hub коннектора не должно останавливать вход в KLIP.
Ни одно из этих изменений не требует изменений на стороне KLIP. K1–K4 не затрагиваются.
Два экземпляра Hub, две регистрации
Зарегистрируйте шлюз отдельно в каждом Hub и сопоставьте их с соответствующим KLIP:
Этап | KLIP_ENV | HUB_ISSUER | Клиент |
4–6 (сборка, staging UAT) |
| тестовый DWS Hub | клиент шлюза в тестовом Hub |
7+ (продуктовый cutover) |
| продуктовый Hub | отдельный клиент шлюза в продуктовом Hub |
Сопоставление проверяется при загрузке, потому что ошибка в одну сторону опасна, а не просто неаккуратна:
KLIP_ENV=production+ тестовыйHUB_ISSUER→ шлюз отказывается запускаться. Иначе любой, кто может создать аккаунт в тестовом Hub, получил бы доступ к реальным коммерческим данным.KLIP_ENV=staging+ продуктовыйHUB_ISSUER→ предупреждение и продолжение работы.
hub:check выводит обнаруженное сопоставление, так что cutover можно проверить:
pairing: KLIP staging <-> Hub testingЧто требует DWS Hub (это не ванильный OIDC)
Согласно Docs/SSO-TARGET-APP-INTEGRATION.md. Четыре пункта отличаются от значений по
умолчанию, которые предполагает библиотека OIDC-клиента, и три привели бы к полному отказу:
DWS Hub | |
Тип клиента | публичный, PKCE S256 — |
Discovery |
|
Тело токена | JSON; форма-кодирование возвращает |
Области | только |
| обязателен в запросе токена и должен совпадать байт-в-байт |
Поэтому HUB_DISCOVERY_URL настраивается явно, HUB_CLIENT_SECRET необязателен,
а HUB_TOKEN_BODY по умолчанию равен json с одноразовым откатом на форму при
unsupported_grant_type (с записью в лог того, что сработало, чтобы это можно было зафиксировать).
Настройка
Зарегистрируйте шлюз как OIDC-клиент в Hub с redirect URI
<PUBLIC_URL>/authorize/hub/callback. Это публичный клиент — не запрашивайте секрет.Поместите
HUB_ISSUER,HUB_DISCOVERY_URLиHUB_CLIENT_IDв/opt/mcp/.env.Проверьте до того, как любой пилотный пользователь попробует:
docker compose exec -T gateway node dist/cli.js hub:checkЭто выводит redirect URI для регистрации, выполняет discovery и предупреждает, если Hub не объявляет PKCE S256. Шлюз также проверяет discovery при загрузке и сообщает о нём в
/healthzкакhub_oidc.Добавьте пилотных пользователей (без паролей — их аутентифицирует Hub):
docker compose exec -T gateway node dist/cli.js user:add someone@example.com "Their Name"
Аварийный аккаунт
Разрешён ровно один локальный аккаунт с паролем, для случаев, когда Hub недоступен или неправильно настроен:
docker compose exec -T gateway node dist/cli.js user:add-break-glass it-emergency@example.comОн скрыт за раскрытием на странице входа, принуждает к смене пароля при первом
использовании, и каждый вход через него аудируется с break_glass: true с высоким
уровнем серьёзности. Пользователь, аутентифицированный через Hub, вообще не может
использовать путь с паролем, так что отключение Hub — это не способ откатиться к паролю,
который никто не задавал.
Установите BREAK_GLASS_ENABLED=false, как только путь через Hub будет подтверждён в production, чтобы полностью исключить пароль как поверхность атаки.
Опциональный групповой контроль — недоступен на DWS Hub
HUB_REQUIRED_GROUP добавляет вторую проверку по claim groups. DWS Hub не выдаёт такой claim (он объявляет только openid profile email), поэтому его установка приведёт к отказу для каждого пользователя. hub:check предупредит об этом. Разрешительный список пилота в таблице users остаётся механизмом контроля авторизации.
Вход, инициированный IdP
Hub может направить пользователя сразу на callback из плитки своей панели управления. Для коннектора так работать не может: callback существует, чтобы завершить запрос авторизации, начатый Claude, поэтому при обращении без такого запроса не остаётся ничего, для чего можно было бы выдать код. Шлюз обнаруживает это и сообщает «начните с Claude», а не завершается ошибкой «вход истёк».
4. Быстрый старт (локально)
npm ci
docker run -d --name mcpgw-devdb -e POSTGRES_DB=gateway -e POSTGRES_USER=gateway \
-e POSTGRES_PASSWORD=devpassword -p 127.0.0.1:55432:5432 postgres:16-alpine
cp .env.example .env.dev # then edit: PUBLIC_URL=http://localhost:8787, DATABASE_URL=...55432...
npx tsx test/fixtures/mockKlip.ts 5099 & # mock KLIP, behaves like the real one
set -a; . ./.env.dev; set +a
npm run migrate
npx tsx src/index.tsСоздайте пилотного пользователя (работает с TTY или вводом через пайп):
printf 'a-strong-password\na-strong-password\n' | npx tsx src/cli.ts user:add you@example.com "Your Name"5. Тесты
npm testНабор тестов | Что покрывает |
| Матрица Incoterm × статус × null, кг→MT, порядок округления, отрицательный остаток, временные метки WIB |
| Исчерпывающая таблица метод/путь для T-6, обходы путей и выходы за пределы origin |
| Конверт T-5, усечение |
| Ограниченная выборка публикует |
| Все 9 инструментов против mock KLIP; ни один не-GET запрос никогда не достигает KLIP; повторный вход после 401; |
| Привязка audience по RFC 8707: принимается только канонический resource этого сервера |
| Маскирование S5 — агрессивно в отношении строк, инертно в отношении чисел |
| Hub OIDC против mock-провайдера: несовпадение issuer в discovery, посторонний ключ подписи, неверный issuer/audience, истёкший токен, отсутствующий и повторно использованный nonce, claim без email |
| Допуск по |
| Production KLIP за тестовым Hub отказывается запускаться; обычные связки — нет |
| Метод аутентификации token-endpoint, выбираемый из discovery, включая Hub, поддерживающие только POST, и Hub с публичным клиентом |
| DWS Hub смоделирован точно: discovery |
Mock Hub — это работающий мини-OIDC-провайдер — настоящий discovery-документ, настоящие JWKS, настоящие RS256 ID-токены и проверка PKCE для кода авторизации — со всеми настройками, необходимыми для подделки плохого токена, потому что негативные сценарии и есть суть.
Фикстура mock KLIP намеренно воспроизводит особенности реальной системы: килограммы, помеченные как MT, статусы на разных языках, incoterm за пределами стандартной четвёрки, количества со значением null, контракт с перепоставкой, limit молча усекаемый до 100, и примечание к контракту, содержащее полезную нагрузку для prompt-инъекции.
6. Развёртывание
Полная процедура для конкретного хоста, с реальным IP, именем хоста и таблицей групп безопасности: deploy/RUNBOOK.md.
# on ECS-MCP
cd /opt/mcp && git pull
docker compose build gateway && docker compose up -d
curl -fsS http://127.0.0.1:8787/healthzАдминистративный CLI — обратите внимание, он запускается внутри контейнера, поскольку на хосте установлен только Docker и нет Node.js:
docker compose exec -T gateway node dist/cli.js user:list
docker compose exec -T gateway node dist/cli.js audit:summary --days 7
docker compose exec -T gateway node dist/cli.js audit:export --from 2026-08-01 --to 2026-09-01 --out /tmp/audit.csv
docker compose exec -T gateway node dist/cli.js routes:verify # probes KLIP, reports Appendix A gapsKill switch (S8) — целевое время: менее 5 минут:
docker compose exec -T gateway node dist/cli.js tokens:revoke-all --reason "incident 2026-xx"
docker compose stop gatewayBreak-glass, если контейнер приложения нездоров:
docker compose exec -T db psql -U gateway -d gateway -c "UPDATE oauth_tokens SET revoked_at=now() WHERE revoked_at IS NULL;"7. Приложение A — это жёсткий барьер
src/adapters/klip/routes.ts и src/adapters/klip/fields.ts содержат каждый путь KLIP, имя query-параметра, максимальный размер страницы, имя поля ответа и значение enum, от которых зависит адаптер. Каждая запись на данный момент не проверена.
Этот барьер исполняемый, а не формальный: при KLIP_ENV=production процесс отказывается запускаться, пока какой-либо маршрут не проверен. Запустите routes:verify на staging, зафиксируйте результаты, установите verified: true для каждого маршрута и enums.verified = true.
Два поля важнее остальных:
`
# | Изменение | Почему |
B3 | Origin отклоняется только при наличии и недействительности | Спецификация требует 403 только для присутствующего и недействительного Origin. Claude обращается к коннектору server-to-server и может не отправлять Origin вовсе; отклонение отсутствующего Origin давало бы 403 на каждый вызов инструмента. |
B4 |
| Привязка аудитории по RFC 8707. В документ PRM добавлены |
B5 | Без состояния транспорт; идентичность из токена на каждый запрос | Редакция от 2026-07-28 убрала сессии протокола. T-4 выполняется по построению — не существует сессии, которую можно перехватить. |
B6 | Аварийный выключатель запускается через | На хосте нет Node.js, поэтому |
B7 | nginx — базовый уровень защиты от злоупотреблений; лимит на пользователя по ключу | Весь трафик приходит из общего диапазона исходящих адресов Anthropic, поэтому ограничение по IP помещает весь пилот в один bucket. Добавлено |
H1 | OAuth построен на | В SDK уже есть AS, включая отзыв токенов и ограничение частоты по умолчанию. Наши — только хранилище и аутентификация пользователей. |
H2 | Вход через downstream Hub OIDC, плюс одна локальная учётная запись break-glass | Вынесено из Фазы 2. Hub аутентифицирует; таблица пользователей остаётся пилотным allowlist. |
H3 | Каркас allowlist в nginx для | Anthropic публикует стабильные диапазоны исходящего трафика и рекомендует allowlist; |
H4 | Усечённые результаты публикуют | Четыре разных пути к уверенно неверному числу. |
H5 | Короткий TTL-кэш; страницы 2..N загружаются конкурентно; размер страницы ограничен | Десять последовательных round-trip'ов не уложатся в P95 ≤ 5 с. |
H6 | Добавлен 9-й инструмент | Без него опечатка в имени завода возвращает пустой набор, который читается как «ничего не осталось». |
H7 |
| Захардкоженная строка «KLIP production» заставила бы каждый staging-ответ UAT утверждать, что это production. |
H9 |
| Удаление партициями — единственное, что допускает триггер append-only; экспорт U5 не имел реализации; IP клиентов иначе записывались бы как 127.0.0.1. |
H10 | Sidecar для резервного копирования, healthcheck контейнера, детали | Описано в TSD, но так и не было реализовано в руководстве, поэтому на момент запуска этого бы не существовало. |
— | Неизвестные параметры инструментов отклоняются через | PRD 8.1 требует отклонения; обычный |
— | Инструменты возвращают | Типизированные данные вместо JSON, встроенного в текст, — меньше ошибок транскрибации, а именно это измеряет M1. |
9. Открытые вопросы
K1–K4 на стороне Hub — роль
MCP_READONLY, учётная записьsvc-mcp, правило security-group.Внешняя синхронизация бэкапов и протестированное восстановление — sidecar пишет только локально.
Реальные корпоративные egress-диапазоны в
deploy/nginx/mcp.confи включение двух закомментированных строкreturn 403.Нагрузочное тестирование на ёмкости 30 одновременных подключений.
Регистрация клиента в тестовом Hub — собственная регистрация коннектора, с URI редиректа
<PUBLIC_URL>/authorize/callback, ключамиHUB_ISSUER,client_id/client_secretHub, подтверждёнными email и группами, затемhub:check. Отдельная регистрация для продакшн-Hub — на этапе 7.Решить, задавать ли
HUB_REQUIRED_GROUPи выключать лиBREAK_GLASS_ENABLEDпосле подтверждения пути через Hub на проде.
This server cannot be installed
Maintenance
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
- FlicenseAqualityBmaintenanceEnables read-only querying of the gong-nl-db Postgres database through natural language via Claude Desktop.9
- AlicenseNot gradedqualityDmaintenanceEnables natural language queries against Snowflake Gold-layer tables through Claude Desktop, allowing users to ask business questions in plain English without SQL knowledge.MIT
- AlicenseNot gradedqualityCmaintenanceGives Claude live access to your Observe tenant, enabling natural language queries about errors, logs, and metrics without writing OPAL pipelines.17MIT
- FlicenseNot gradedqualityBmaintenanceEnables natural language interaction with Kintone data via Claude, allowing listing apps, field definitions, querying and modifying records.
Related MCP Connectors
WHOOP recovery, strain, sleep and workouts in Claude via official WHOOP OAuth. Free, open source.
Connect Claude to Fathom meeting recordings, transcripts, and summaries
Garmin data in Claude & ChatGPT via the Garmin Health API. OAuth sign-in, no password sharing.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/dwsitproject-hub/MCP-Gateway'
If you have feedback or need assistance with the MCP directory API, please join our Discord server