mcp-server

mcp-server

A universal MCP server for registering internal, external, and OpenAPI-based APIs as MCP tools. It exposes them to MCP clients via Streamable HTTP and provides admin portal, RBAC/session auth, credential injection, and audit logging.

Category
访问服务器

README

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절 참고).

2. 주요 기능

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

  • MCP Tool discovery — DB에 등록된 도구를 운영자 노출 정책·사용자별 선택·Scope에 따라 tools/list로 동적 노출 (app/mcp/registry.py, app/mcp/discoverability.py)
  • MCP Tool execution — tools/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_active 및 deleted_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/me — Authorization: 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.py의 Settings 기준(실제 코드 우선). 실제 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.ini의 sqlalchemy.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 확인, CMD는 uvicorn 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.py의 length=32 컬럼들이 영향받으며, 이로 인해 앱 import·OpenAPI 생성·pytest 수집이 모두 실패합니다.
  • [타입/정의 충돌] app/services/execution/credential_service.py에서 CredentialResolver 이름이 중복 정의(Protocol vs 구현 클래스)되어 mypy가 no-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 파일 추가를 권장합니다(임의로 라이선스를 지정하지 않았습니다).

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选