Skip to main content
Glama

MCP Server

범용 MCP(Model Context Protocol) 서버입니다. 내부/외부/OpenAPI 기반 API를 MCP Tool로 등록하고, MCP 클라이언트(예: Codex, Claude 등 MCP 지원 클라이언트)에 Streamable HTTP 전송으로 노출합니다. 관리자 Portal, RBAC/세션 인증, OpenAPI Import, 외부 Credential Service 연동을 통한 Credential 주입, 감사 로그를 제공합니다.

문서 기준: 이 README는 저장소의 실제 소스코드(app/, alembic/, pyproject.toml, Dockerfile, docker-compose.yml, Jenkinsfile, .env.example/env.template)를 직접 분석해 작성되었습니다. 코드로 확인되지 않은 내용은 "확인 불가"로 표기합니다.


1. 프로젝트 소개

  • 목적: 이질적인 백엔드 API들(수동 등록 / 내부 핸들러 / OpenAPI 문서 Import)을 하나의 MCP 서버로 통합해, MCP 클라이언트가 표준 프로토콜(tools/list, tools/call)로 도구를 발견·실행할 수 있게 합니다.

  • 해결하는 문제: 도구별로 제각각인 인증/노출/정책을 서버가 중앙에서 관리하고, 위험도·Scope·사용자별 선택·확인(confirmation) 정책과 감사 로그를 일관되게 적용합니다.

  • MCP Server의 역할: 도구 레지스트리 + 정책 게이트 + 실행 오케스트레이터. 업스트림 API로의 HTTP 실행과 감사 로깅을 담당합니다.

  • Credential Service와의 관계: 실제 자격증명(Secret)은 이 서버가 보관하지 않습니다. 외부 Credential Service(HTTP API)를 호출해 호출자 신원을 확인(/api/v1/me)하고, 필요 시 단기 토큰 교환 후 Credential을 reveal 받아 업스트림 요청에 주입합니다. MCP Server는 HashiCorp Vault를 직접 호출하지 않습니다(아래 3·6절 참고).


Related MCP server: OpenAPI MCP Server

2. 주요 기능

실제 구현이 확인된 기능만 기재합니다. 다수 실행·인증 기능은 feature flag로 감싸여 있고 코드 기본값은 보수적으로 off입니다(11절 표 참고).

  • MCP Tool discovery — DB에 등록된 도구를 운영자 노출 정책·사용자별 선택·Scope에 따라 tools/list로 동적 노출 (app/mcp/registry.py, app/mcp/discoverability.py)

  • MCP Tool executiontools/call 시 다단계 정책 게이트 통과 후 업스트림 HTTP 호출 (app/services/execution/service.py, http_dispatcher.py) — 기본 비활성(MCP_TOOL_EXECUTION_ENABLED=false)

  • OpenAPI Import — OpenAPI 문서(URL/파일)를 가져와 도구를 생성/동기화. 신규 도구는 비활성/비노출로 들어와 관리자가 검토 후 활성화 (app/api/v1/openapi_sources.py)

  • 사용자별 Tool 선택 — 사용자가 자신에게 노출할 도구를 선택 (user_tool_settings, app/services/user_tool_service.py)

  • 관리자 Tool/Provider 관리 — 등록·활성화·MCP 노출·Portal 노출·정책 설정·삭제 (app/api/v1/tools.py, providers.py)

  • Scope 및 위험도 정책 — 도구별 required_scopes, RiskLevel, requires_confirmation (app/db/models/policy.py, app/domain/scopes.py)

  • Credential 주입 — REQUEST_HEADER_PASSTHROUGH / Credential Service reveal 주입 (app/services/execution/) — 기본 비활성

  • 감사 로그 — 모든 tools/list/tools/call을 RECEIVED→완료로 기록, Secret 마스킹 (app/db/models/audit.py, app/services/execution/audit.py)

  • 관리자 Portal / 사용자 Portal — 서버사이드 렌더링 HTML(Jinja2) + 세션 인증 (app/admin/, app/web/)


3. 전체 아키텍처

flowchart LR
    Client[MCP Client]
    Server[MCP Server<br/>FastAPI + MCP SDK]
    DB[(PostgreSQL<br/>schema: mcp)]
    Cred[Credential Service<br/>외부 HTTP API]
    Upstream[업스트림 Tool API<br/>HTTP]
    Vault[HashiCorp Vault]

    Client -- "Streamable HTTP /mcp/" --> Server
    Server -- "SQLAlchemy async / asyncpg" --> DB
    Server -- "/me, token/exchange, reveal" --> Cred
    Server -- "도구 실행(HTTP)" --> Upstream
    Cred -. "이 저장소 범위 밖(코드로 확인 불가)" .-> Vault
  • MCP Server는 Credential Service만 호출하며, HashiCorp Vault로의 직접 연결선은 없습니다(코드 전반에서 hvac/VAULT_ADDR/VAULT_TOKEN/hashicorp 사용 0건).

  • Credential Service가 내부적으로 Vault를 쓰는지는 이 저장소 코드로 확인할 수 없어 점선·"확인 불가"로 표기했습니다.

계층(레이어)

  • app/api, app/web, app/admin — HTTP 진입점(REST v1 / 세션 웹 / 관리자 HTML)

  • app/mcp — MCP 전송·서버 핸들러·도구 매핑·발견/정렬

  • app/services — 실행·인증·감사·사용자도구·OpenAPI 등 도메인 서비스

  • app/repositories — DB 접근 계층

  • app/db, alembic — 모델/스키마/마이그레이션

  • app/domain, app/dto, app/core — 열거형·스코프·DTO·설정·로깅·예외


4. 요청 처리 흐름

실제 코드(app/mcp/server.py, app/services/execution/service.py) 기준입니다.

tools/list

MCP 요청 (/mcp/)
→ DB 세션 확인 (없으면 registry_unavailable 오류)
→ (MCP_MCP_CALLER_AUTH_REQUIRED=true일 때) 호출자 인증: API Key 헤더 → Credential Service GET /api/v1/me → status=ACTIVE 확인
→ Principal 생성(McpPrincipal: scopes/roles/email/status)
→ 사용자별 Tool 선택 정책 적용(ADMIN=전체, USER=본인 enable 목록, 매핑 없으면 0개 = fail-closed)
→ 운영자 노출 정책(SQL): provider/tool enabled·미삭제·ACTIVE, tool.mcp_exposed=true, sync_status≠STALE, policy enabled
→ Scope 필터: required_scopes ⊆ caller scopes 인 도구만
→ 입력 스키마 검증 통과분만
→ 페이지네이션(cursor, 상한 MCP_MCP_MAX_DISCOVERABLE_TOOLS) → 목록 반환
  • 인증 실패 시 DENIED 감사 로그 기록 후 오류 반환.

tools/call

MCP 요청 (/mcp/)
→ RECEIVED 감사 로그 기록
→ (필요 시) 호출자 인증
→ 전역 실행 플래그 확인(MCP_TOOL_EXECUTION_ENABLED)
→ 도구 조회(노출명) / 삭제·비활성 확인
→ MCP 노출 게이트(enforce_mcp_exposed)
→ 사용자별 선택 게이트(USER는 본인 enable 도구만)
→ Provider/Policy 게이트
→ 실행 타입 게이트(HTTP만 지원)
→ Scope 인가(required_scopes 검사)
→ 인자 검증(OFF/WARN/STRICT)
→ 확인 게이트(requires_confirmation=true & confirm≠true → TOOL_CONFIRMATION_REQUIRED) *Secret 호출 이전*
→ Credential 주입 판단(NONE / REQUEST_HEADER_PASSTHROUGH / CREDENTIAL_SERVICE_CREDENTIAL_INJECTION)
→ 업스트림 HTTP 실행(HttpToolDispatcher)
→ 결과 Secret 마스킹(fail-closed) → 감사 로그 완료 기록 → 결과 반환
  • 감사 기록은 fail-open(감사 실패가 응답을 막지 않음), 그 외 정책은 대부분 fail-closed.

  • 구분해야 할 개념: 전역 MCP 호출자 인증(누가 호출했나 — Credential Service /me) ≠ 도구별 upstream credential 주입(어떤 도구에 자격증명을 어떻게 붙이나). REQUEST_HEADER_PASSTHROUGH(inbound 헤더 변환)와 CREDENTIAL_SERVICE_CREDENTIAL_INJECTION(reveal 값 QUERY 주입)은 별개입니다.


5. 인증 및 권한 구조

방식

헤더/전송

적용 대상

Credential Service API Key (cs_...)

X-Credential-Service-Api-Key (기본, MCP_CREDENTIAL_SERVICE_API_KEY_HEADER)

MCP 요청(/mcp/)

Bearer(cs_... API Key 또는 CS 발급 JWT)

Authorization: Bearer <token>

REST 관리 API

세션 쿠키 + CSRF

SessionMiddleware 쿠키 + X-CSRF-Token

관리자/사용자 웹 Portal

  • API Key prefix: cs_ (app/api/rest_auth.py). cs_로 시작하면 API Key 경로로 처리.

  • JWT: MCP는 JWT를 발급하지 않고 검증만 합니다. 알고리즘 HS256(대칭키) 전용, 발급자/청중은 MCP_JWT_ISSUER/MCP_JWT_AUDIENCE(기본 credential-service), 시크릿은 MCP_JWT_SECRET(32자 이상 필수). REST 경로는 /me 재확인으로 JWT sub와 일치하는지도 검증.

  • Principal: Credential Service /me 프로필로 생성(McpPrincipal/RestPrincipal) — scopes/roles/email/status 등 보유.

  • Scope(정확히 7개, app/domain/scopes.py): resources:read, credentials:reveal, servers.read, ssh.execute, logs.read, status.read, disk.read. 정확 일치(와일드카드/대소문자 무시 없음).

  • Role(UserRole): USER, ADMIN. REST 관리 API·관리자 페이지는 ADMIN 필요. 마지막 활성 관리자 강등/비활성 및 자기 자신 강등 방지.

  • 상태 검사: MCP는 /me status=ACTIVE, REST/웹은 McpUser.is_activedeleted_at IS NULL.

  • 인증 실패 응답: CS 비활성 → 403, 무효/취소/검증불가(타임아웃·5xx 포함) → 401, JWT 설정 불가 → 503, 미등록/비활성 사용자 → 403, DB 미구성 → 503.

  • Secret 처리 원칙: 원본 자격증명/Authorization/API Key는 로그·감사에 저장하지 않음. 예시 키는 placeholder(cs_<...>)만 사용.

기본값 주의: MCP_MCP_CALLER_AUTH_REQUIRED, MCP_WEB_AUTH_ENABLED, MCP_JWT_ENABLED는 모두 기본 false입니다. 운영에서는 명시적으로 활성화해야 합니다.


6. Credential Service 연동

MCP Server와 Credential Service의 책임 경계:

  • MCP Server: 정책 판단, 도구 실행, 감사. 자격증명 원본을 저장하지 않음.

  • Credential Service(외부): 사용자 신원(/api/v1/me), API Key→단기 JWT 교환(/api/v1/auth/token/exchange), Credential reveal(/api/v1/my/resources/{resource_id}/credentials/reveal).

동작:

  • 신원 확인: GET {base}/api/v1/meAuthorization: ApiKey <cs_...> 또는 Bearer <JWT>.

  • 토큰 교환: 요청의 cs_ API Key → 단기 Exchange JWT(Authorization: ApiKey <cs_...>로 요청). TTL 상한 3600초, 재시도 없음.

  • Credential 주입: CREDENTIAL_SERVICE_CREDENTIAL_INJECTION은 (교환 JWT로) reveal 후 QUERY 파라미터로만 주입. REQUEST_HEADER_PASSTHROUGH는 허용 목록 헤더를 업스트림 허용 헤더로 전달(CR/LF 인젝션 차단).

  • 평문 노출 방지: reveal 호출은 단기 Bearer JWT로만(원본 cs_ 키는 reveal 엔드포인트로 전송하지 않음). reveal 값은 요청 스코프에서 기억되어 외부 반환 경계에서 마스킹(fail-closed).

  • 타임아웃/재시도: connect 5.0s / read 10.0s / token-exchange 10.0s, 응답 상한 256KiB, 재시도 없음.

  • 관련 환경변수: MCP_CREDENTIAL_SERVICE_BASE_URL, MCP_AUTH_BASE_URL(미설정 시 base_url fallback), MCP_CREDENTIAL_SERVICE_API_KEY_HEADER, MCP_CREDENTIAL_SERVICE_TOKEN_EXCHANGE_ENABLED/_PATH, MCP_CREDENTIAL_SERVICE_CREDENTIAL_INJECTION_ENABLED, MCP_MCP_CALLER_AUTH_REQUIRED 등(11절 표 참고).

HashiCorp Vault 직접 연결 없음: 이 저장소 코드는 Vault SDK/주소/토큰을 전혀 사용하지 않습니다. 자격증명 접근은 오직 위 3개 Credential Service HTTP 엔드포인트를 통합니다.


7. 데이터베이스 구조

  • 엔진/ORM: PostgreSQL + SQLAlchemy 2.0 비동기(asyncpg). DB 미구성 시 "bootstrap 모드"로 기동(엔진 없음).

  • 전용 스키마: mcp(MCP_DATABASE_SCHEMA). 모든 테이블이 스키마 한정, search_path도 해당 스키마로 고정(ADR-0004). 스키마명 검증 정규식 ^[a-z][a-z0-9_]{0,62}$, 예약/공용 스키마 거부.

  • 공통 mixin: UUID PK, 소프트 삭제(deleted_at), 타임스탬프. 활성 유니크 제약은 대개 WHERE deleted_at IS NULL 부분 유니크.

핵심 엔티티 관계:

erDiagram
  mcp_providers ||--o{ mcp_tools : "provider_id (RESTRICT)"
  mcp_providers ||--o| mcp_openapi_sources : "provider_id (CASCADE, unique)"
  mcp_tools ||--o| mcp_tool_http_configs : "tool_id (CASCADE, 1:1)"
  mcp_tools ||--o| mcp_tool_policies : "tool_id (CASCADE, 1:1)"
  mcp_users ||--o{ user_tool_settings : "user_id (CASCADE)"
  mcp_tools ||--o{ user_tool_settings : "tool_id (CASCADE)"
  mcp_users ||--o{ mcp_users : "created_by_user_id (SET NULL)"
  mcp_tools ||--o{ mcp_audit_logs : "tool_id (SET NULL)"
  mcp_providers ||--o{ mcp_audit_logs : "provider_id (SET NULL)"

모델

테이블

설명

Provider

mcp_providers

도구 제공자(업스트림). 인증 설정 보유

Tool

mcp_tools

도구. mcp_exposed/portal_exposed/인증 override 등

ToolHttpConfig

mcp_tool_http_configs

도구 HTTP 실행 설정(1:1)

ToolPolicy

mcp_tool_policies

정책(category/risk/required_scopes/requires_confirmation)(1:1)

OpenApiSource

mcp_openapi_sources

Provider의 OpenAPI Import 소스(0..1)

McpUser

mcp_users

로컬 사용자(USER/ADMIN), Argon2id 해시

UserToolSetting

user_tool_settings

사용자별 도구 enable(is_enabled)

AuditLog

mcp_audit_logs

감사 로그(append-only, 스냅샷 보존, 삭제 시 FK는 SET NULL)

  • 감사 로그: RECEIVED 행 생성 후 완료 시 상태/결과 UPDATE. 원본 자격증명/응답 본문은 저장하지 않고 마스킹된 인자·요약만 저장. 보존/TTL 정책은 코드에 없음(확인 불가).

  • 정책 저장: required_scopes(JSONB, 기본 [])는 저장·검증하지만 강제는 실행 서비스 게이트에서 수행. requires_confirmation은 저장만 하며, 대화형 elicitation 프로토콜이 아니라 실행 게이트(confirm 인자)로 적용.


8. 프로젝트 디렉터리 구조

핵심 경로만 표기합니다.

mcp-server/
├─ app/
│  ├─ main.py            # FastAPI 팩토리(create_app), MCP 마운트, 미들웨어/OpenAPI 커스터마이즈
│  ├─ core/              # config(설정), logging(로깅·Secret redact), security, exceptions
│  ├─ api/               # REST 진입점: rest_auth, deps, health, v1/(tools, providers, users, ...)
│  ├─ web/               # 세션 로그인·Portal 라우트·RBAC 가드
│  ├─ admin/             # 관리자 HTML 라우트 + templates/(SSR)
│  ├─ mcp/               # MCP transport(Streamable HTTP)·server 핸들러·registry·tool_mapper·sanitizer
│  ├─ services/          # execution(실행/인증/감사/credential), auth(jwt/principal), user_tool_service, openapi
│  ├─ repositories/      # DB 접근 계층
│  ├─ db/                # base, session, schema, models/(providers/tools/policy/audit/users ...)
│  ├─ domain/            # enums, scopes, provider/credential_binding
│  ├─ dto/               # 요청/응답 DTO
│  └─ scripts/           # seed_initial_users
├─ alembic/              # env.py(스키마 한정 마이그레이션) + versions/(리비전 13개)
├─ tests/                # unit / integration / e2e / fixtures + conftest.py
├─ docs/                 # architecture / operations / decisions(ADR) / TODO.md
├─ scripts/              # ci_check.sh, phase 검증 스크립트, update_kustomize_image_tag.sh
├─ Dockerfile           # 멀티스테이지(python:3.12-slim, uv)
├─ docker-compose.yml   # 로컬 db(postgres:16) + app
├─ Jenkinsfile          # CI(빌드/푸시/배포 repo 갱신) — 값은 예시 placeholder
├─ pyproject.toml       # 의존성/도구 설정(ruff/mypy/pytest)
└─ uv.lock

9. 요구 환경

  • OS: Linux / WSL(개발), 컨테이너는 python:3.12-slim

  • Python: >=3.12 (pyproject.toml)

  • 패키지 매니저: uv(astral), uv.lock 사용

  • PostgreSQL: 실제 DB 기능 사용 시 필요(compose는 postgres:16-alpine)

  • 선택적 외부 서비스: Credential Service(자격증명 연동 사용 시)

  • Docker / Docker Compose: 로컬 실행·빌드 시(선택)

주요 라이브러리: FastAPI, uvicorn, MCP SDK(mcp), SQLAlchemy 2.0(asyncio)+asyncpg, Alembic, pydantic/pydantic-settings, httpx, PyJWT(검증), pwdlib[argon2], itsdangerous, jsonschema, Jinja2.


10. 로컬 실행

명령은 프로젝트 루트에서 실행합니다. 표기 [WSL zsh]는 로컬 셸입니다.

  1. 저장소 Clone

    # [WSL zsh]
    git clone <YOUR_REPO_URL> mcp-server && cd mcp-server
  2. 의존성 설치

    # [WSL zsh]
    uv sync --dev
  3. 환경변수 준비 (템플릿 복사 후 값 채우기; 실제 Secret은 커밋 금지)

    # [WSL zsh]
    cp .env.example .env
  4. DB 준비 (로컬 Postgres 컨테이너)

    # [Docker]
    docker compose up -d db
  5. 마이그레이션 (DB 접속 환경변수 설정 후)

    # [WSL zsh]
    uv run alembic upgrade head
  6. (선택) 초기 사용자 seed — MCP_SEED_* 환경변수 설정 후

    # [WSL zsh]
    uv run python -m app.scripts.seed_initial_users
  7. 서버 실행

    # [WSL zsh]
    uv run uvicorn app.main:app --host 0.0.0.0 --port 8080
  8. health 확인

    # [WSL zsh]
    curl http://127.0.0.1:8080/health
    curl http://127.0.0.1:8080/ready
  9. OpenAPI / MCP 확인

    # [WSL zsh]
    curl http://127.0.0.1:8080/openapi.json   # Swagger UI: http://127.0.0.1:8080/docs
    # MCP 엔드포인트(정규 URL): http://127.0.0.1:8080/mcp/  (Streamable HTTP)

⚠ 현재 소스 상태에서는 아래 18절의 알려진 이슈(Enum 길이)로 인해 애플리케이션 import가 실패합니다. 위 실행 명령들은 코드 정의상 유효하지만, 실제 기동을 위해서는 해당 이슈 해결이 선행되어야 합니다.


11. 환경변수

접두사 MCP_ + 대문자 필드명. 값은 app/core/config.pySettings 기준(실제 코드 우선). 실제 Secret 값은 표기하지 않습니다. "필수"는 미설정 시 해당 기능이 동작하지 않는 값을 의미합니다.

애플리케이션

변수

필수

기본값

설명

민감

MCP_APP_NAME

아니오

mcp-server

서비스명

아니오

MCP_VERSION

아니오

0.1.0

버전

아니오

MCP_ENV

아니오

local

local/dev/prod

아니오

MCP_LOG_LEVEL

아니오

INFO

로그 레벨

아니오

MCP_HOST

아니오

0.0.0.0

bind host

아니오

MCP_PORT

아니오

8080

bind port

아니오

MCP_DATABASE_SCHEMA

아니오

mcp

전용 PG 스키마(검증됨)

아니오

데이터베이스

변수

필수

기본값

설명

민감

MCP_DB_HOST

DB 사용 시

(없음)

PG 호스트

아니오

MCP_DB_PORT

아니오

5432

PG 포트

아니오

MCP_DB_USER

DB 사용 시

(없음)

PG 사용자

아니오

MCP_DB_PASSWORD

DB 사용 시

(없음)

PG 비밀번호

MCP_DB_NAME

DB 사용 시

(없음)

PG DB명

아니오

인증 / JWT / 세션

변수

필수

기본값

설명

민감

MCP_WEB_AUTH_ENABLED

아니오

false

웹/관리자 인증 활성

아니오

MCP_SESSION_SECRET

web_auth 시 필수

(없음)

세션 서명 시크릿

MCP_SESSION_COOKIE_NAME

아니오

mcp_session

세션 쿠키명

아니오

MCP_SESSION_TTL_SECONDS

아니오

3600

세션 TTL

아니오

MCP_COOKIE_SECURE

아니오

false

Secure 쿠키

아니오

MCP_COOKIE_SAMESITE

아니오

lax

SameSite

아니오

MCP_PASSWORD_MIN_LENGTH

아니오

8

비밀번호 최소 길이

아니오

MCP_PASSWORD_MAX_LENGTH

아니오

128

비밀번호 최대 길이

아니오

MCP_JWT_ENABLED

아니오

false

JWT 검증 활성

아니오

MCP_JWT_ALGORITHM

아니오

HS256

HS256만 허용

아니오

MCP_JWT_SECRET

jwt 시 필수

(없음)

JWT 검증 시크릿(≥32자)

MCP_JWT_ISSUER

아니오

credential-service

발급자

아니오

MCP_JWT_AUDIENCE

아니오

credential-service

청중

아니오

MCP

변수

필수

기본값

설명

MCP_MCP_ENABLED

아니오

true

MCP 전송 마운트

MCP_MCP_PATH

아니오

/mcp

마운트 경로(정규 URL /mcp/)

MCP_MCP_MAX_DISCOVERABLE_TOOLS

아니오

500

노출 도구 상한

MCP_MCP_PAGE_SIZE

아니오

100

페이지 크기

MCP_MCP_JSON_RESPONSE

아니오

false

단일 JSON vs SSE

MCP_MCP_DNS_REBINDING_PROTECTION

아니오

false

Host 헤더 보호

MCP_MCP_ALLOWED_HOSTS

아니오

[]

허용 Host(콤마 구분)

MCP_MCP_CALLER_AUTH_REQUIRED

아니오

false

MCP 호출자 인증 요구

Credential Service

변수

필수

기본값

설명

민감

MCP_CREDENTIAL_SERVICE_BASE_URL

연동 시

(없음)

Credential Service base URL

아니오

MCP_AUTH_BASE_URL

아니오

(없음, base_url fallback)

인증(/me·exchange) base URL

아니오

MCP_CREDENTIAL_SERVICE_API_KEY_HEADER

아니오

X-Credential-Service-Api-Key

요청 API Key 헤더명

아니오

MCP_CREDENTIAL_SERVICE_CREDENTIAL_INJECTION_ENABLED

아니오

false

reveal 주입 활성

아니오

MCP_CREDENTIAL_SERVICE_TOKEN_EXCHANGE_ENABLED

아니오

false

토큰 교환 활성

아니오

MCP_CREDENTIAL_SERVICE_TOKEN_EXCHANGE_PATH

아니오

/api/v1/auth/token/exchange

교환 경로

아니오

MCP_CREDENTIAL_SERVICE_PROFILE_PATH

아니오

/api/v1/me

프로필 경로

아니오

MCP_CREDENTIAL_SERVICE_CONNECT_TIMEOUT_SECONDS

아니오

5.0

connect timeout

아니오

MCP_CREDENTIAL_SERVICE_READ_TIMEOUT_SECONDS

아니오

10.0

read timeout

아니오

MCP_CREDENTIAL_SERVICE_TOKEN_EXCHANGE_TIMEOUT_SECONDS

아니오

10.0

교환 timeout

아니오

MCP_CREDENTIAL_SERVICE_MAX_RESPONSE_BYTES

아니오

262144

응답 상한

아니오

MCP_ALLOWED_CREDENTIAL_SERVICE_CREDENTIAL_FIELDS

아니오

api_key,token,refresh_token,client_secret,webhook_secret

reveal 허용 필드

아니오

Tool 정책 / Credential passthrough

변수

기본값

설명

MCP_TOOL_EXECUTION_ENABLED

false

도구 실행 전역 활성

MCP_REQUEST_CREDENTIAL_PASSTHROUGH_ENABLED

false

헤더 passthrough 활성

MCP_ALLOWED_CREDENTIAL_SOURCE_HEADERS

[X-Credential-Service-Api-Key]

소스 헤더 허용목록

MCP_ALLOWED_PASSTHROUGH_TARGET_HEADERS

[Authorization]

대상 헤더 허용목록

MCP_HTTP_EXEC_CONNECT_TIMEOUT_SECONDS

5.0

실행 connect timeout

MCP_HTTP_EXEC_MAX_RESPONSE_BYTES

2097152

실행 응답 상한

감사 로그

변수

기본값

설명

MCP_AUDIT_LOG_ENABLED

true

감사 로그 활성

MCP_AUDIT_MAX_TEXT_LENGTH

2000

텍스트 최대 길이

OpenAPI Import

MCP_OPENAPI_FETCH_CONNECT_TIMEOUT_SECONDS(5.0), MCP_OPENAPI_FETCH_READ_TIMEOUT_SECONDS(15.0), MCP_OPENAPI_MAX_DOCUMENT_BYTES(5242880), MCP_OPENAPI_MAX_UPLOAD_BYTES(5242880), MCP_OPENAPI_MAX_REDIRECTS(3), MCP_OPENAPI_ALLOW_PRIVATE_NETWORKS(false), MCP_OPENAPI_MAX_OPERATIONS(500), MCP_OPENAPI_MAX_REF_DEPTH(20), MCP_OPENAPI_MAX_SCHEMA_DEPTH(30).

개발/테스트 (Settings 클래스 밖, seed 스크립트가 직접 사용)

MCP_SEED_ADMIN_USERNAME, MCP_SEED_ADMIN_PASSWORD(민감), MCP_SEED_USER_USERNAME, MCP_SEED_USER_PASSWORD(민감), MCP_SEED_UPDATE_EXISTING(기본 false).

코드·템플릿 불일치: .env.example/env.template는 위 변수 중 일부(예: 웹 세션 그룹, 일부 Credential Service 세부 변수, MCP_AUTH_BASE_URL, MCP_VERSION, MCP_SEED_*)를 생략합니다. 생략된 변수는 코드 기본값을 따릅니다.


12. 데이터베이스 Migration

  • 도구: Alembic(비동기 엔진). alembic.inisqlalchemy.url은 비워두고 alembic/env.py가 앱 설정에서 주입.

  • 스키마 한정: version_table_schema=mcp, autogenerate가 mcp 스키마만 대상으로 함(다른 서비스 객체 보호). 온라인 실행 시 마이그레이션 전에 스키마를 별도 트랜잭션으로 생성.

  • 리비전 수: alembic/versions/ 13개. 각 리비전에 upgrade()/downgrade() 구현되어 있음(스텁 아님).

# [WSL zsh]  (DB 접속 환경변수 설정 후)
uv run alembic upgrade head              # 최신 스키마 적용
uv run alembic revision --autogenerate -m "메시지"   # 리비전 생성(개발)
  • downgrade: 리비전에 구현되어 있으나 프로젝트 문서/CI에서 운영 downgrade 절차는 규정하지 않음. 운영 적용 시 스키마 격리·별도 트랜잭션 생성 특성을 확인하세요.

  • 컨테이너/CI는 마이그레이션을 자동 실행하지 않습니다(Dockerfile·Jenkins에 alembic 실행 없음).


13. 테스트 및 품질 검사

# [WSL zsh]
uv run pytest                    # 전체 테스트 (testpaths=tests, asyncio_mode=auto)
uv run pytest -m integration     # integration 마커만 (PostgreSQL 필요)
uv run pytest tests/unit/test_config.py   # 특정 파일
uv run ruff check .              # lint
uv run ruff format --check .     # 포맷 검사
uv run ruff format .             # 포맷 적용
uv run mypy app                  # 타입 체크(strict)
bash scripts/ci_check.sh         # 전체 CI 게이트(포맷→lint→mypy→pytest→phase 검증)
  • 커버리지 설정([tool.coverage])은 없음. [project.scripts] 콘솔 진입점 없음.

  • 현재 소스 상태의 실제 실행 결과는 18절 참조(일부 FAIL/BLOCKED).


14. Docker 실행

# [Docker]
docker compose up -d db          # 로컬 Postgres만
docker compose up                # db + app 함께
docker build -t mcp-server:dev . # 이미지 빌드(멀티스테이지)
  • Dockerfile: 멀티스테이지(python:3.12-slim), 비루트 app 사용자, uv sync --frozen --no-dev, EXPOSE 8080, HEALTHCHECK/health 확인, CMDuvicorn app.main:app --host 0.0.0.0 --port 8080. 컨테이너는 마이그레이션을 실행하지 않음.

  • docker-compose.yml: db(postgres:16-alpine, 5432, healthcheck pg_isready, 볼륨 mcp_pgdata), app(Dockerfile 빌드, db healthy 후 기동, MCP_DB_HOST=db, 8080 노출). DB 비밀번호 기본값은 로컬 개발용입니다.


15. Kubernetes 및 배포

  • 이 저장소에는 범용 Kubernetes/Kustomize/Argo CD manifest가 포함되어 있지 않습니다. 실제 배포 manifest는 외부 배포 저장소에 존재하는 것으로 참조만 됩니다.

  • 포함된 것: Jenkinsfile(빌드→레지스트리 푸시→배포 repo의 이미지 태그 갱신), scripts/update_kustomize_image_tag.sh(kustomization의 newTag 한 줄만 안전 치환).

  • 주의: Jenkinsfile의 레지스트리/배포 repo/자격증명 ID 등은 실제 값이 아닌 예시 placeholder(registry.example.com:8082, github.com/example-org/..., jenkins@example.com 등)입니다. 실제 운영 값으로 교체가 필요합니다.

  • GitHub Actions(.github/) 없음. Redis/메시지 브로커 없음. HashiCorp Vault 직접 연동 없음.


16. API 및 MCP 사용 예시

placeholder만 사용합니다(유효한 실제 키/Secret 아님).

health:

# [WSL zsh]
curl http://127.0.0.1:8080/health
curl http://127.0.0.1:8080/ready

시스템 정보(무인증):

# [WSL zsh]
curl http://127.0.0.1:8080/api/v1/system/info
curl http://127.0.0.1:8080/api/v1/system/mcp

관리 REST API(ADMIN, Bearer):

# [WSL zsh]
curl -H "Authorization: Bearer cs_<YOUR_API_KEY>" http://127.0.0.1:8080/api/v1/tools

MCP 클라이언트 연결(개념 — 실제 클라이언트 설정 형식에 맞게):

transport : Streamable HTTP
url       : http://127.0.0.1:8080/mcp/
header    : X-Credential-Service-Api-Key: cs_<YOUR_API_KEY>   # MCP_MCP_CALLER_AUTH_REQUIRED=true 인 경우
  • tools/list / tools/call은 MCP 클라이언트가 위 엔드포인트로 JSON-RPC를 전송하면 서버가 4절 흐름대로 처리합니다.

  • 관리자 UI: http://127.0.0.1:8080/admin · 사용자 Portal: http://127.0.0.1:8080/portal/tools (웹 인증 활성 시 로그인 필요).


17. 보안 주의사항

  • API Key / JWT Secret: .env에만 두고 저장소에 커밋하지 마세요. MCP_JWT_SECRET은 32자 이상 필수, placeholder 값은 기동 시 거부됩니다.

  • 로그/감사 내 Secret: 로거는 필드명 기반으로 Secret을 마스킹(***REDACTED***)하고, httpx/httpcore 로그를 WARNING으로 낮춰 쿼리스트링 유출을 억제합니다. 감사 로그는 원본 자격증명/응답 본문을 저장하지 않습니다.

  • Credential injection / reveal: reveal은 단기 Bearer JWT로만 호출하고 원본 API Key를 reveal 엔드포인트로 보내지 않습니다. 반환 경계에서 Secret을 마스킹(fail-closed)합니다.

  • 운영 HTTPS / 쿠키: 운영에서는 MCP_COOKIE_SECURE=true 및 TLS 종단(리버스 프록시)을 사용하세요.

  • MCP 전송 보안: MCP 마운트는 네트워크 계층에서 인증되지 않습니다. 네트워크 ACL/프록시로 보호하고, 외부 노출 시 MCP_MCP_DNS_REBINDING_PROTECTION+MCP_MCP_ALLOWED_HOSTS 설정을 검토하세요.

  • CORS: 저장소 코드에서 별도 CORS 미들웨어 설정은 확인되지 않았습니다(확인 불가). 필요 시 리버스 프록시/게이트웨이에서 통제하세요.

  • DB 자격증명: MCP_DB_PASSWORD는 민감정보입니다. compose 기본 비밀번호는 로컬 전용입니다.

  • HashiCorp Vault 토큰: 이 서버는 Vault 토큰을 사용하지 않습니다(직접 연동 없음).

  • 관리자 기능: 마지막 활성 관리자 보호·자기 강등 방지 등 안전장치가 있으나, MCP_WEB_AUTH_ENABLED=false(기본)에서는 웹 인증이 우회되므로 운영에서 반드시 활성화하세요.

  • 공개 저장소 Secret 관리: 실제 IP/도메인/자격증명을 README·코드·커밋에 포함하지 마세요.


18. 현재 제한 사항

코드 분석과 경량 검증(의존성 설치 후 실제 실행)에서 확인된 사실입니다.

  • [치명적] 애플리케이션 import 실패AuthType의 값 CREDENTIAL_SERVICE_CREDENTIAL_INJECTION(39자)이 DB 컬럼 정의 SAEnum(..., length=32)보다 길어 SQLAlchemy가 ValueError: length must be larger or equal than the length of the longest enum value. 32 < 39를 발생시킵니다. app/db/models/provider.py·policy.py·tool.pylength=32 컬럼들이 영향받으며, 이로 인해 앱 import·OpenAPI 생성·pytest 수집이 모두 실패합니다.

  • [타입/정의 충돌] app/services/execution/credential_service.py에서 CredentialResolver 이름이 중복 정의(Protocol vs 구현 클래스)되어 mypyno-redef/Cannot instantiate protocol을 보고합니다.

  • [품질 검사 드리프트] ruff check(E501 라인 길이 등)·ruff format --check(다수 파일 포맷 차이)·mypy가 현재 통과하지 않습니다.

  • 문서·코드 주석 불일치: app/mcp/의 일부 주석은 "tools/call 미구현(Phase 5)"이라고 되어 있으나, 실제 코드는 플래그 활성 시 업스트림 HTTP 실행·Credential 주입을 수행합니다.

  • 로깅 표기: 로깅 모듈 docstring은 "구조적(structured)"이라 하지만 실제는 stdout 평문 포맷입니다.

  • 감사 로그 보존 정책 없음(append-only, TTL/파기 미구현).

  • 외부 의존 검증 불가: 실제 DB/Credential Service 연결이 필요한 동작은 이 문서 작성 범위에서 검증하지 않았습니다.

위 이슈들은 README 작성 범위(문서만 수정)에서 코드를 변경하지 않았습니다. 별도 수정이 필요합니다.


19. 라이선스

  • 저장소에 별도의 LICENSE 파일은 없습니다.

  • pyproject.toml에는 license = { text = "Proprietary" }로 선언되어 있습니다.

라이선스 정책이 확정되지 않았다면 공개 전에 명시적인 LICENSE 파일 추가를 권장합니다(임의로 라이선스를 지정하지 않았습니다).

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    A generic MCP server that dynamically converts OpenAPI-defined REST APIs into tools for LLMs like Claude. It supports multiple authentication methods and transport protocols, enabling seamless interaction with any OpenAPI-compliant API.
    21 npm
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Self-hosted MCP proxy and aggregation platform. Register multiple upstream MCP servers and expose them through a single unified endpoint with namespace routing, multi-transport support (HTTP/SSE, stdio, OpenAPI→MCP), per-tool overrides, and a web admin UI.
    17
    MIT