mcp-stepup-gateway
mcp-stepup-gateway
원격 클라이언트(Claude.ai, Custom Connector 경유)가 enquire-mcp로 보호되는 Obsidian 볼트를 읽거나 쓰기 전에 요구 시 패스키(WebAuthn) -- "step-up auth" -- 를 요구하는 MCP 게이트웨이입니다. Google 로그인과 허용 목록(mcp-oauth-gateway에서와 같이)은 누가 연결할 수 있는지 결정합니다. 이 프로젝트는 툴별로, 그 사람이 신원을 다시 증명하지 않고 무엇을 할 수 있고, 무엇이 새로운 패스키 터치를 요구하는지 결정합니다.
구체적인 사례에서 시작되었습니다. mcp-oauth-gateway/enquire-mcp-gateway는 이미 "누가 연결하는지 인증"(OAuth + 허용 목록)을 해결합니다. 빠져 있었던 것은 두 번째 계층입니다. 허용 목록 안에서도 모든 툴 호출이 똑같이 자유로워서는 안 됩니다. 노트를 읽는 것은 저렴합니다. 하지만 프롬프트 인젝션 아래에 있을 수 있는 LLM을 통해 볼트 내용을 삭제하거나 다시 쓰는 것은 그렇지 않습니다. 이 게이트웨이는 enquire-mcp 자체를 건드리지 않고 그 구분을 추가합니다.
존재 이유
OAuth로 인증된 원격 MCP 클라이언트는 여전히 볼트의 관점에서 "전체 액세스 권한을 가진 LLM"입니다. 이것은 두 축에서 문제입니다.
LLM은 조작될 수 있습니다. 노트나 툴 응답의 악성 콘텐츠는 에이전트에게 항목을 삭제하거나 덮어쓰도록 지시하려고 시도할 수 있습니다. 프롬프트 인젝션은 가상이 아닙니다.
"한 번 인증됨"은 "영원히 권한 부여됨"을 의미하지 않아야 합니다. 장기 실행 OAuth 세션은 새로운 인간 존재 증명 없이 동일한 LLM에게 무기한 제한 없는 쓰기 권한을 주어서는 안 됩니다.
여기서의 해결책은 툴별 위험 수준 모델과, 읽기를 승인하는 수명이 짧은 능력 핸들(15분), 그리고 쓰기나 삭제를 승인하는 호출별 패스키 확인입니다. -- 서버가 받은 실제 인수에서 렌더링되며, LLM이 제어하는 텍스트에서는 결코 렌더링되지 않습니다.
아키텍처
Cliente MCP remoto (Claude.ai, via Custom Connector)
│ HTTPS (OAuth Google + allowlist -- fora do escopo deste
│ README; ver mcp-oauth-gateway/enquire-mcp-gateway)
▼
┌───────────────────────────────────────────────────────────┐
│ gateway │
│ │
│ StepUpMiddleware -- por tool call: │
│ 1. policy.yaml decide o nivel (0/1/2) da tool │
│ 2. L0 (tools de auth) -- sempre passa │
│ 3. L1 (leitura) -- exige handle de sessao valido │
│ (senao devolve AUTH_REQUIRED + URL de unlock) │
│ 4. L2 (escrita/delete) -- exige confirmacao fresca │
│ por chamada (args_digest HMAC liga a aprovacao aos │
│ argumentos EXATOS; senao devolve CONFIRMATION_REQUIRED) │
│ │
│ Tools injetadas (nivel 0, sempre disponiveis): │
│ vault_auth_unlock / vault_auth_check / vault_auth_status │
└──────────────────────────┬───────────────────────────────────┘
│ Streamable HTTP + bearer
▼
┌───────────────────────────────────────────────────────────┐
│ auth-service │
│ │
│ WebAuthn (passkey) -- registro, challenges de unlock e de │
│ confirmacao, sessoes (SQLite), audit log append-only. │
│ So alcancavel via rotas /internal (X-Gateway-Key) do │
│ gateway, ou pelas telas publicas /unlock, /confirm, │
│ /register (esta ultima so com token de bootstrap). │
└──────────────────────────┬───────────────────────────────────┘
│ nunca fala com o backend
│ diretamente -- so autentica
▼
(o handle/token volta ao Claude via
gateway, que entao repassa a chamada
original ao backend)
│
▼
┌───────────────────────────────────────────────────────────┐
│ backend │
│ enquire-mcp (serve-http, vault Obsidian) │
└───────────────────────────────────────────────────────────┘gateway는 어떤 자격 증명도 저장하지 않습니다 -- auth-service(내부 라우트, GATEWAY_KEY로 인증됨)와만 대화하여 "이 핸들이 이 툴을 승인합니까?" 또는 "이 확인이 정확히 이 인수를 승인했습니까?"라고 묻습니다. 인간은 채팅에 아무것도 입력하거나 붙여넣지 않습니다. 전체 패스키 의식은 auth-service가 제공하는 URL에서 브라우저에서 일어납니다.
위험 수준
수준 | 요구 사항 | 예시 |
L0 | 없음 -- 항상 허용 |
|
L1 | 유효한 세션 핸들(절대 TTL 15분, 유휴 5분) |
|
L2 |
|
|
policies/policy.yaml은 백엔드의 각 툴을 수준에 매핑합니다. Deny-by-default: 명시적으로 매핑되지 않은 모든 툴은 가장 제한적인 수준(default_level: 2)으로 떨어집니다. -- enquire-mcp가 업데이트에서 새 툴을 얻으면(백엔드는 npx -y로 실행되므로 시작할 때마다 버전이 바뀔 수 있음), 그 툴은 열린 상태가 아니라 보호된 상태로 도착합니다. 사용된 툴 이름의 출처와 프로덕션 전에 실제로 검증해야 할 사항은 policies/policy.yaml 자체의 주석을 참조하십시오.
설정
Docker와 Docker Compose가 필요합니다. 세 서비스(gateway, auth-service, backend)가 함께 올라옵니다.
1. 환경 변수
cp .env.example .env # Windows: Copy-Item .env.example .env저장소 루트에서 채우십시오:
Google OAuth (
GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET,PUBLIC_BASE_URL,ALLOWED_EMAILS) --mcp-oauth-gateway와 동일한 패턴입니다. Google Cloud Console에서 OAuth Client를 만드는 단계별 안내는 해당 프로젝트의 README를 참조하십시오.WebAuthn (
WEBAUTHN_RP_ID,WEBAUTHN_RP_NAME,PUBLIC_ORIGIN,GATEWAY_KEY,DIGEST_KEY) --WEBAUTHN_RP_ID를 설정하기 전에 아래 경고를 참조하십시오.GATEWAY_KEY와DIGEST_KEY는openssl rand -hex 32로 생성하십시오.Backend (
BACKEND_BEARER_TOKEN,OBSIDIAN_VAULT_PATH) --gateway와backend사이에 공유되는 토큰과 보호할 Obsidian 볼트의 호스트 경로입니다.
WEBAUTHN_RP_ID는 영구적입니다. 등록된 각 패스키의 WebAuthn 서명 자체에 포함되는 도메인(포트 없음, 프로토콜 없음)입니다. 첫 등록 후 이 값을 변경하면 모든 패스키가 무효화됩니다 -- 모든 사람이 새 부트스트랩으로 다시 등록해야 합니다. 첫 패스키를 등록하기 전에 최종 도메인(PUBLIC_BASE_URL과 동일한 호스트,https://제외)을 결정하십시오. 나중이 아니라.auth-service는 이 변수가 설정되지 않으면 시작을 거부합니다(src/authsvc/config.py) -- 의도적입니다. 여기서 조용한 기본값은 부팅 실패보다 나쁠 것입니다.
2. 스택 올리기
docker compose --env-file .env -f docker/docker-compose.yml up -d --build
docker compose --env-file .env -f docker/docker-compose.yml logs -f auth-service--env-file .env는 선택 사항이 아닙니다 -- Docker Compose는 compose 파일의 자체 디렉터리(docker/)를 기준으로 ${VAR}를 해석하며, 저장소 루트가 아닙니다. 이 플래그 없이 실행하면 OBSIDIAN_VAULT_PATH가 실제 볼트 대신 조용한 폴백(docker/vault, 비어 있음)으로 떨어지며, 표시되는 오류가 없습니다. 전체 세부 사항은 docker/docker-compose.yml 상단의 Uso: 주석을 참조하십시오(Task 17 리뷰에서 발견됨).
3. 첫 번째 패스키 등록(부트스트랩)
auth-service 로그에서 다음을 찾으십시오:
[bootstrap] token de registro (10 min): <token>패스키가 있는 장치(휴대폰 또는 호환되는 비밀번호 관리자)의 브라우저에서 <PUBLIC_BASE_URL>/register?t=<token>을(를) 열고 등록을 완료하십시오. 토큰은 10분 후에 만료됩니다. 기한을 놓치면 auth-service를 다시 시작(docker compose restart auth-service)하여 다른 토큰을 생성하십시오. -- 이렇게 하면 보류 중인 세션/챌린지도 지워집니다(SESSION_PURGE_ON_START=true 기본값).
최소 두 개의 패스키를 등록하십시오 (예: 휴대폰 + 비밀번호 관리자) 부트스트랩 토큰이 아직 유효한 동안. 이것은 "장치를 분실했습니다"에 대한 이 프로젝트의 완화 조치입니다. 복구 코드는 없습니다(의도적 결정; 설계 사양의 미결 결정 섹션 참조).
4. 사용자 지정 커넥터로 연결
claude.ai -> Settings -> Connectors -> Add custom connector에서 <PUBLIC_BASE_URL>/mcp를 붙여넣으십시오. OAuth Client 필드는 비워 두십시오(동적 등록). ALLOWED_EMAILS에 있는 Google 계정으로 로그인한 후, 전체 검증 절차(잠금 해제, 읽기, 확인을 통한 쓰기, 두 대화 테스트)는 tests/integration/test_e2e_manual.md에 있습니다.
알려진 제한 사항
A8 -- 15분 창 안에 같은 대화를 여는 B는 핸들을 상속받습니다. 이것은 핸들 모델의 실제 구멍이며, 이미 문서화되었고 설계상 수용된 것입니다. 세션 핸들(L1)은 그 순간 대화를 읽는 사람의 신원에 연결되지 않고, 그것이 태어난 대화에만 연결됩니다. Claude 계정이 공유되고 B가 A가 잠금 해제한 같은 대화를 여는 경우(새 대화가 아니라) 절대 TTL 15분(또는 유휴 5분) 안에, B는 A가 얻은 읽기 능력(L1)을 상속합니다. 짧은 TTL, 유휴 시간 초과, 클라이언트가 안정적으로 제공할 때
Mcp-Session-Id에 대한 추가 바인딩으로 완화되지만 제거되지는 않습니다. 쓰기(L2)는 어떤 경우에도 B에게 도달할 수 없습니다. 호출마다 새로운 패스키 서명이 필요하기 때문입니다. 전체 위협 분석은 설계 사양의 A8 섹션(docs/superpowers/specs/2026-08-16-mcp-stepup-auth-proxy-design.md)을 참조하십시오. 이것은 조용히 수정될 버그가 아닙니다 -- 대화별 공유 핸들 모델의 알려진 제한 사항이며,tests/integration/test_e2e_manual.md의 절차 7단계는 별개의 경우(새 대화)가 올바르게 차단되었음을 증명하기 위해 존재합니다.Rate limiting(속도 제한)은 어떤 요청 경로에도 연결되어 있지 않습니다.
src/authsvc/ratelimit.py모듈(메모리 내 슬라이딩 윈도우,Janela클래스)은 존재하고 자체 테스트가 있지만,auth-service나gateway의 어떤 라우트도 그것을 인스턴스화하거나 호출하지 않습니다 -- "연결"되어 있지 않습니다. 실제로 이는 설계 사양의 섹션 20(보안 테스트)과 섹션 14(프롬프트 인젝션 보호, 항목 4)에 설명된 "핸들 무차별 대입" 및 "볼트 체계적 스캔" 완화가 기본 코드가 준비되었음에도 불구하고 프로덕션에는 아직 존재하지 않음을 의미합니다. 이것은 이 프로젝트의 다른 어떤 제어로도 커버되지 않는 실제 공백입니다.policies/policy.yaml에는 예시 값(level_1: { calls: 60, window_s: 300 })이 있는rate_limits섹션이 있지만, 현재gateway_main.py나src/stepup/middleware.py에는 이러한 값을 읽어 실제로 호출을 제한하는 것이 없습니다. 이 게이트웨이를 실제 볼륨 사용(신뢰할 수 있는 단일 사용자뿐만 아니라)에 노출하기 전에ratelimit.Janela를 L1 경로에 연결하고(이상적으로는auth-service의 challenge/확인 시도에도) 폴리싱이 아닌 우선순위로 취급해야 합니다.기타 구조적 제한 사항(프로세스 감독 없음, 호출자별 범위 없는 공유
BACKEND_BEARER_TOKEN비밀, 공개 노출에는 자체 터널 필요)은 이 프로젝트가 OAuth/허용 목록 계층을 상속하는mcp-oauth-gateway와 동일합니다. -- 자세한 내용은 해당 프로젝트의 README를 참조하십시오.
테스트
# Windows
.venv\Scripts\pytest.exe -v
# Linux/macOS
.venv/bin/pytest -v다음을 포함합니다: 권한 부여 정책(src/stepup/policy.py), step-up 미들웨어(수준, AUTH_REQUIRED/CONFIRMATION_REQUIRED), auth-service(WebAuthn, 세션, 챌린지, 확인, 감사 로그, HMAC 다이제스트), 그리고 docker-compose.yml 구성 해석(누락된 --env-file .env의 두 가지 오류 모드 포함).
실제 MCP 클라이언트와 실제 패스키에 대한 종단 간 절차는 이 스위트에 없습니다 -- tests/integration/test_e2e_manual.md를 참조하십시오.
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 Connectors
Remote MCP for Copilot CLI switch gate MCP, structured receipts, audit logs, and reviewer-ready evid
Self-hosted federated MCP gateway: one OAuth 2.1 MCP server in front of N apps, user-level scopes.
MCP server teaching AI agents to implement TideCloak: auth, E2EE, IGA, security analysis
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/joaorura/mcp-stepup-gateway'
If you have feedback or need assistance with the MCP directory API, please join our Discord server