MCP Gateway
MCP 게이트웨이 — 1단계(KLIP, 읽기 전용)
권한이 부여된 Energi-Up 직원이 애플리케이션을 열지 않고도 Claude를 통해 자연어로 KLIP을 조회할 수 있게 해주는 단일 보안 서비스입니다. 엄격히 읽기 전용입니다.
배포 대상(PRD Q3, 현재 마감됨): <gateway-hostname> -> <gateway-public-ip>
(ECS-MCP, ap-southeast-5). 호스트 이름은 mcp-gw이며, v0.9 문서에서 가정했던
mcp.example.com이 아님에 유의하십시오. PRD/TSD도 이에 맞게 업데이트되어야 합니다.
문서: PRD v0.9 · TSD v0.9 · 구현 가이드 · 설계 검토 · 배포 런북
1. 고정 버전(T-1)
MCP 스펙 개정판과 SDK 버전이 여기에 고정되어 있습니다. SDK 자체 문서가 설계 문서와 일치하지 않는 경우 SDK가 우선합니다 — SDK가 정확한 API를 정의합니다.
구성 요소 | 고정 버전 | 참고 사항 |
| 1.30.0(정확히, 캐럿 없음) | 2026년 7월 27일 게시. OAuth AS, Streamable HTTP 전송 및 |
MCP 프로토콜 개정판 | 2025-11-25 구현, | 개정판 |
Node.js | 22 LTS( | |
TypeScript | 5.9.3, | |
express | 5.2.1 | TSD의 4.x에서 상향 조정됨: SDK가 |
zod | 4.4.3 | TSD의 3.x에서 상향 조정됨: SDK의 타입 정의가 zod 4를 대상으로 합니다. 이제 |
jose | 6.2.9 | RS256 게이트웨이 토큰. |
| 2.1.0 |
|
axios | 1.19.0 | KLIP 클라이언트, 메서드 가드 뒤에 위치. |
pg | 8.23.0 | |
PostgreSQL | 16(컨테이너) | |
nginx | 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. 인증 — 다운스트림 허브 OIDC
파일럿 사용자는 Downstream Hub(OIDC)로 로그인합니다. 게이트웨이는 Claude가
통신하는 권한 부여 서버로 유지되며, Hub는 게이트웨이 자체 /authorize 흐름의
한 단계일 뿐입니다.
우리는 의도적으로 SDK의 ProxyOAuthServerProvider를 사용하지 않습니다. 프록시를
사용하면 Claude에게 Hub 토큰이 전달되어 RFC 8707 오디언스 바인딩이 깨지고, Claude에게
klip:read보다 더 넓은 Hub 범위가 부여되며, 토큰 발급이 우리의 통제를 벗어나 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 교환이 실행됩니다. 혼동하지 마십시오. Claude 자체의
code_challenge는 Claude→게이트웨이 구간을 보호하고(SDK가 처리), 게이트웨이가
서버 측에 보관하는 별도의 검증자(verifier)는 게이트웨이→Hub 구간을 보호합니다.
어떤 Hub 클라이언트인가? KLIP의 것이 아닌 게이트웨이 자체의 클라이언트
MCP 게이트웨이용 새 OIDC 클라이언트를 등록하십시오. KLIP이 이미 테스트 DWS Hub에 등록되어 있더라도 KLIP의 Hub 등록을 재사용하지 마십시오.
Hub는 두 신뢰 경계 중 하나만 다룹니다:
경계 | 자격 증명 | Hub 관련 여부? |
Claude → 게이트웨이(어떤 사람이 질문하는가) | 게이트웨이 자체 Hub 클라이언트 + 파일럿 허용 목록 | 예 |
게이트웨이 → KLIP(데이터 읽기) | KLIP의 | 아니요 — PRD §7에서 명시적으로 제외 |
KLIP의 클라이언트를 재사용하면 첫 번째 경계가 구체적인 방식으로 깨집니다. 게이트웨이는
ID 토큰의 aud를 자체 HUB_CLIENT_ID와 대조하여 검증합니다. KLIP의 클라이언트 ID를
공유한다는 것은 KLIP 로그인 중에 발급된 ID 토큰이 게이트웨이에서 수락된다는 뜻이며,
이는 혼동된 대리인(confused deputy) 형태입니다. 별도의 클라이언트가 있어야 두 신뢰
당사자를 구분할 수 있습니다. 또한 리다이렉트 URI 허용 목록, 클라이언트 시크릿, 교체
일정, Hub SSO 감사 항목, 비활성화 스위치를 독립적으로 유지할 수 있습니다 — 커넥터의
Hub 클라이언트를 끄는 것이 KLIP 로그인을 중단시켜서는 안 됩니다.
이 중 어느 것도 KLIP 측의 변경이 필요하지 않습니다. K1–K4는 영향을 받지 않습니다.
두 개의 Hub 인스턴스, 두 개의 등록
각 Hub에 게이트웨이를 별도로 등록하고 일치하는 KLIP과 페어링하십시오:
단계 | KLIP_ENV | HUB_ISSUER | 클라이언트 |
4–6(빌드, 스테이징 UAT) |
| 테스트 DWS Hub | 테스트 Hub의 게이트웨이 클라이언트 |
7+(프로덕션 전환) |
| 프로덕션 Hub | 프로덕션 Hub의 별도 게이트웨이 클라이언트 |
이 페어링은 부팅 시 강제됩니다. 한 방향으로 잘못 구성하면 지저분한 것이 아니라 위험하기 때문입니다:
KLIP_ENV=production+ 테스트HUB_ISSUER→ 게이트웨이가 시작을 거부합니다. 테스트 Hub 계정을 만들 수 있는 사람이라면 누구든 실제 상용 데이터에 접근할 수 있기 때문입니다.KLIP_ENV=staging+ 프로덕션HUB_ISSUER→ 경고 후 계속 진행합니다.
hub:check는 감지된 페어링을 출력하므로 전환이 검증 가능합니다:
pairing: KLIP staging <-> Hub testingDWS Hub가 요구하는 사항(일반 OIDC가 아님)
Docs/SSO-TARGET-APP-INTEGRATION.md에 따름. 이 중 네 가지는 OIDC 클라이언트
라이브러리가 가정하는 기본값과 다르며, 세 가지는 완전히 실패할 수 있습니다:
DWS Hub | |
클라이언트 유형 | 공개(public), PKCE S256 — |
디스커버리 |
|
토큰 본문 | JSON; 폼 인코딩은 |
범위 |
|
| 토큰 요청에서 필수이며 바이트 단위로 정확해야 함 |
따라서 HUB_DISCOVERY_URL은 명시적으로 구성되고, HUB_CLIENT_SECRET은 선택 사항이며,
HUB_TOKEN_BODY는 기본값이 json이고 unsupported_grant_type 발생 시 폼으로의
일회성 폴백이 있습니다(어느 것이 작동했는지 로깅하여 고정할 수 있음).
설정 방법
리다이렉트 URI
<PUBLIC_URL>/authorize/hub/callback로 Hub에 게이트웨이를 OIDC 클라이언트로 등록하십시오. 공개 클라이언트이므로 시크릿을 요청하지 마십시오.HUB_ISSUER,HUB_DISCOVERY_URL,HUB_CLIENT_ID를/opt/mcp/.env에 넣으십시오.파일럿 사용자가 시도하기 전에 검증하십시오:
docker compose exec -T gateway node dist/cli.js hub:check이 명령은 등록할 리다이렉트 URI를 출력하고 디스커버리를 실행하며, Hub가 S256 PKCE를 광고하지 않으면 경고합니다. 게이트웨이는 또한 부팅 시 디스커버리를 프로브하고
/healthz에hub_oidc로 보고합니다.파일럿 사용자를 추가하십시오(비밀번호 없음 — Hub가 인증함):
docker compose exec -T gateway node dist/cli.js user:add someone@example.com "Their Name"
비상(break-glass) 계정
Hub가 다운되거나 잘못 구성된 경우를 위해 정확히 하나의 로컬 비밀번호 계정이 허용됩니다:
docker compose exec -T gateway node dist/cli.js user:add-break-glass it-emergency@example.com이 계정은 로그인 페이지의 공개 고지 뒤에 숨겨져 있으며, 첫 사용 시 비밀번호 변경을
강제하고, 이를 통한 모든 로그인은 높은 심각도로 break_glass: true로 감사됩니다.
Hub 인증 사용자는 비밀번호 경로를 전혀 사용할 수 없으므로, Hub를 끄는 것이 아무도
설정하지 않은 비밀번호로 폴백하는 방법이 될 수 없습니다.
Hub 경로가 프로덕션에서 입증되면 BREAK_GLASS_ENABLED=false로 설정하여
비밀번호 노출 영역을 완전히 제거하십시오.
선택적 그룹 게이트 — DWS Hub에서는 사용할 수 없음
HUB_REQUIRED_GROUP은 groups 클레임에 대한 두 번째 검사를 추가합니다. DWS Hub는
이 클레임을 발급하지 않으므로(오직 openid profile email만 광고함) 설정하면 모든
사용자가 거부됩니다. 그렇게 하면 hub:check가 경고합니다. users 테이블의 파일럿 허용
목록이 계속 인가 통제 수단으로 남습니다.
IdP 시작 로그인
Hub는 대시보드 타일에서 사용자를 콜백으로 바로 보낼 수 있습니다. 커넥터에서는 이 방식이 작동하지 않습니다. 콜백은 Claude가 시작한 인가 요청을 완료하기 위해 존재하므로, 인가 요청 없이 도착하면 코드를 발급할 대상이 없기 때문입니다. 게이트웨이는 이를 감지하여 "sign-in expired"로 실패 처리하는 대신 "start from Claude instead"라고 안내합니다.
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 testSuite | 다루는 내용 |
| Incoterm × 상태 × null 매트릭스, kg→MT, 반올림 순서, 음수 잔량, WIB 타임스탬프 |
| T-6에 대한 메서드/경로 전체 표, 경로 순회 및 출처 이스케이프 |
| T-5 봉투(envelope), |
| 제한된 페치(fetch)는 |
| 목 KLIP을 상대로 한 9개 도구 전체; 비-GET 요청은 어떤 것도 KLIP에 도달하지 않음; 401 재로그인; |
| RFC 8707 오디언스 바인딩: 이 서버의 정식 리소스만 허용됨 |
| S5 리댁션 — 문자열에는 적극적, 숫자에는 무영향 |
| 목 제공자를 상대로 한 Hub OIDC: 디스커버리 발급자 불일치, 외부 서명 키, 잘못된 발급자/오디언스, 만료된 토큰, 누락 및 재사용된 nonce, 이메일 클레임 없음 |
|
|
| 테스트 Hub 뒤에 있는 프로덕션 KLIP은 부팅을 거부함; 정상 페어링은 거부하지 않음 |
| 디스커버리에서 선택된 토큰 엔드포인트 인증 방법, post 전용 및 공개 클라이언트 Hub 포함 |
| DWS Hub를 정확히 모델링: |
목 Hub는 실제로 동작하는 미니 OIDC 제공자입니다. 실제 디스커버리 문서, 실제 JWKS, 실제 RS256 ID 토큰, 인가 코드에 대한 PKCE 검증을 갖추고 있으며, 잘못된 토큰을 위조하는 데 필요한 모든 조절 장치를 갖추고 있습니다. 부정 사례가 바로 핵심이기 때문입니다.
목 KLIP 픽스처는 실제 시스템의 특이점을 의도적으로 재현합니다: MT로 표시된 킬로그램,
혼합 언어 상태, 표준 네 가지에 속하지 않는 인코텀, null 수량, 초과 납품 계약,
조용히 100으로 제한되는 limit, 프롬프트 인젝션 페이로드를 담은 계약 비고.
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 gaps킬 스위치(S8) — 목표 5분 이내:
docker compose exec -T gateway node dist/cli.js tokens:revoke-all --reason "incident 2026-xx"
docker compose stop gateway앱 컨테이너가 비정상(unhealthy)이면 break-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 경로, 쿼리 매개변수 이름, 페이지 크기 상한, 응답 필드 이름 및 열거형 값이
들어 있습니다. 현재 모든 항목이 검증되지 않았습니다.
이 게이트는 문서상 절차가 아니라 실행 가능한 게이트입니다. KLIP_ENV=production에서는
경로가 하나라도 검증되지 않으면 프로세스가 시작을 거부합니다. 스테이징을 대상으로
routes:verify를 실행하고 결과를 기록한 다음, 각 경로에 verified: true를 설정하고
enums.verified = true를 설정하세요.
나머지보다 더 중요한 두 필드가 있습니다:
maxLimit— KLIP이 실제로 수용하는 최대limit. 만약 100으로 조용히 제한된다면,KLIP_PAGE_SIZE가 1000일 때 한 페이지가 열 페이지로 늘어나 지연 시간 목표를 깨뜨립니다.enums.*— 정식 상태 값과 인코텀 값. 매핑되지 않은 값은 기본값으로 절대 처리되지 않으며 데이터 품질 주석과 함께 합계에서 제외됩니다.
8. TSD v0.9와의 차이점
각 항목은 설계 검토에서 비롯되었으며 해당 호출 지점에 주석으로 달려 있습니다.
# | 변경 | 이유 |
B3 | Origin은 존재하고 유효하지 않은 경우에만 거부됨 | 사양은 제시되었고 유효하지 않은 Origin에 대해서만 403을 요구한다. Claude는 커넥터를 서버 간으로 호출하며 아무것도 보내지 않을 수 있다. 누락된 경우를 거부하면 모든 도구 호출이 403이 된다. |
B4 |
| RFC 8707 audience 바인딩. PRM 문서에 |
B5 | 무상태 전송; 요청별 토큰에서 신원 확인 | 2026-07-28 개정판에서 프로토콜 세션이 제거되었다. T-4는 구조적으로 충족된다 — 가로챌 세션이 없다. |
B6 | Kill switch는 | 호스트에 Node.js가 없으므로 |
B7 | nginx는 남용 방지의 최소 기준이며, 사용자별 한도는 OAuth | 모든 트래픽은 Anthropic의 공유 이그레스 범위에서 들어오므로, IP 키 기반 제한은 전체 파일럿을 하나의 버킷에 넣는다. |
H1 | OAuth는 SDK의 | SDK는 폐기 및 기본 rate limiting을 포함한 AS를 제공한다. 우리가 담당하는 것은 저장소와 사용자 인증뿐이다. |
H2 | 다운스트림 Hub OIDC가 로그인 경로이며, break-glass 로컬 계정 하나가 있다 | Phase 2에서 앞당겨 가져왔다. Hub가 인증하고, users 테이블은 파일럿 허용 목록으로 유지된다. |
H3 | Anthropic의 | Anthropic은 안정적인 이그레스 범위를 공개하고 허용 목록을 권장한다. |
H4 | 잘린 결과는 | 자신 있게 틀린 숫자로 가는 네 가지 별개의 경로. |
H5 | 짧은 TTL 캐시, 2..N 페이지는 동시에 가져오고, 페이지 크기는 | 순차적 왕복 10회로는 P95 ≤ 5초를 충족할 수 없다. |
H6 | 9번째 도구 | 이것이 없으면 오타가 있는 플랜트 이름이 빈 집합을 반환하고, 이는 "미결제 없음"으로 읽힌다. |
H7 |
| 명세에 하드코딩된 "KLIP production" 문자열은 모든 스테이징 UAT 응답이 프로덕션이라고 주장하게 만들었을 것이다. |
H9 |
| 파티션 삭제를 통한 보존은 append-only 트리거가 허용하는 유일한 방법이다. U5의 export는 구현이 없었고, 클라이언트 IP는 모두 127.0.0.1로 기록되었을 것이다. |
H10 | 백업 사이드카, 컨테이너 healthcheck, | TSD에는 명시되었지만 가이드에는 구현되지 않아서, go-live 시점에 존재하지 않았을 것이다. |
— | 알 수 없는 도구 매개변수는 | PRD 8.1은 거부를 요구한다. 일반 |
— | 도구는 | 프로스에 내장된 JSON이 아니라 타입이 있는 데이터 — 전사 오류가 줄어든다. 이것이 M1이 측정하는 것이다. |
9. 아직 남은 사항
Appendix A 조정 (P1) — 프로덕션을 차단하며, 시작 시 강제된다.
KLIP 측 K1–K4 —
MCP_READONLY역할,svc-mcp계정, 보안 그룹 규칙.호스트 외부 백업 동기화 및 검증된 복원 — 사이드카는 로컬에만 쓴다.
deploy/nginx/mcp.conf에 실제 기업 이그레스 CIDR을 넣은 다음, 주석 처리된 두 줄의return 403을 활성화한다.부하 테스트 — 30명 동시 사용자 용량 NFR.
Hub 클라이언트 등록 — 게이트웨이 자체의 것을 먼저 테스트 DWS Hub에:
HUB_ISSUER, 클라이언트 ID와 시크릿, 리다이렉트 URI<PUBLIC_URL>/authorize/hub/callback을 Hub 측에 등록한다. Hub의 email 및 groups 클레임 이름을 확인한 다음hub:check를 실행한다. 프로덕션 Hub에는 Stage 7에서 두 번째 별도 등록이 필요하다.HUB_REQUIRED_GROUP을 설정할지, 그리고 Hub 경로가 프로덕션에서 입증된 후BREAK_GLASS_ENABLED=false로 전환할지 결정한다.
This server cannot be deployed
Maintenance
Related MCP Connectors
Query your org's data in natural language — read-only MCP access to SQL, NoSQL, files & warehouses.
Query your warehouse or a CSV with Claude/ChatGPT over MCP, governed by table-level ACL + audit.
Query 40 databases from Claude, ChatGPT, or Cursor — on any device. Read-only, encrypted, audited.
Give Claude only the Google Drive files you choose. Every action logged.
Related MCP Servers
- AlicenseAqualityCmaintenanceEnables read-only querying of the gong-nl-db Postgres database through natural language via Claude Desktop.9Academic Free v1.1
- 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
- FlicenseNot gradedqualityCmaintenanceEnables natural language interaction with Kintone data via Claude, allowing listing apps, field definitions, querying and modifying records.-
- FlicenseAqualityCmaintenanceEnables querying AgroClimate company data via natural language through ChatGPT or Claude.7-