Skip to main content
Glama
README.md
# MCP Mock Lab

Agent 개발자가 MCP 목 서버의 요청·응답 계약을 등록하고, 바로 연결할 수 있는 FastAPI + FastMCP 대시보드입니다. `main.py`가 시작점이며 기본 포트는 **8888**입니다.

화면별 기능과 사용 규칙은 [FEATURES.md](FEATURES.md)에 정리했습니다.

API 없이 화면 동작만 공개할 때는 [docs/](docs/) 폴더를 GitHub Pages의 배포 폴더로 선택하면 됩니다. 이 정적 데모는 등록·호출 데이터를 서버에 저장하거나 MCP endpoint를 제공하지 않습니다.

## 실행

Python 3.13을 준비한 뒤 다음을 실행합니다.

```bash
uv sync --all-groups
uv run python main.py
```

- 대시보드: `http://localhost:8888`
- API 문서: `http://localhost:8888/docs`
- liveness: `http://localhost:8888/api/health/live`
- readiness: `http://localhost:8888/api/health/ready`

기본 관리자 계정은 `admin` / `admin123!@#` 입니다. 운영 환경에서는 반드시 환경 변수로 교체하세요.

```bash
export MCP_ADMIN_USERNAME=admin
export MCP_ADMIN_PASSWORD='a-strong-password'
export MCP_SESSION_SECRET='a-long-random-secret'
export MCP_PUBLIC_BASE_URL='https://mcp.example.com'
export MCP_COOKIE_SECURE=true
uv run python main.py
```

관리자는 대시보드의 **운영 설정**에서 MCP Base URL을 저장할 수 있습니다. 기본값은 `http://localhost:8888`이며, 예를 들어 `https://mcp.example.com`으로 바꾸면 이후 MCP 연결 주소는 `https://mcp.example.com/mcp/{slug}`로 생성됩니다.

## 사용 흐름

1. **MCP 등록** 탭에서 MCP 서버의 이름, slug, MCP 담당자 이름·사번을 입력합니다. 작성 중인 내용은 브라우저에 임시저장할 수 있습니다.
2. 그 MCP 아래에 여러 **Tool**을 추가하고, Tool별 Request/Response 필드와 JSON 예시를 정의합니다. 예를 들어 `weather` MCP 안에 `yesterday_weather`, `today_weather`, `tomorrow_weather` Tool을 둘 수 있습니다.
3. `weather` slug를 등록하면 모든 Tool을 제공하는 Streamable HTTP MCP endpoint가 `http://localhost:8888/mcp/weather`로 생성됩니다.
4. 카탈로그의 **연결 JSON**을 복사해 MCP 클라이언트 설정에 넣습니다. Tool별 브라우저 테스트 주소는 `/api/mock/mcps/weather/tools/{tool_name}/invoke` 입니다.
5. **시나리오**를 먼저 등록한 뒤 MCP 등록/수정 화면에서 `UC-AB-01` 같은 시나리오를 복수 선택해 연결합니다.
6. 각 Tool마다 DB, EAI, OCR, HTTP 등의 **Tool 분류**를 하나 선택하고 Request/Response를 정의합니다.
7. 반복되는 Tool 계약은 **Tool 입출력 템플릿**으로 저장한 뒤, 등록 화면에서 선택한 Tool에 적용합니다.
8. **호출 이력** 탭에서 KST 일자와 MCP별로 요청·응답, trace ID, 자동 보정, 지연시간, 성공·오류와 MCP·Tool별 호출 그래프를 조회합니다.

등록자가 입력한 Request/Response 예시가 타입과 맞지 않거나 JSON이 잘못되어도 서버는 `string`, `integer`, `number`, `boolean`, `array`, `object` 타입에 맞는 더미값으로 해당 필드만 보정합니다. 호출 응답의 `fallback_fields`에서 보정된 필드를 확인할 수 있습니다.

## 데이터와 가용성

현재는 요청 처리 성능을 위해 등록 정보를 메모리에서 읽고, **등록·수정·삭제 때만** `data/registry.json`에 원자적으로 저장합니다. 고빈도 MCP 호출은 `slug → MCP → Tool` 프로세스 내 인덱스(L1 캐시)에서 바로 찾으므로 요청·테스트 경로는 디스크를 읽지 않습니다. FastMCP endpoint는 무상태 HTTP로 동작합니다.

Python에는 Java virtual thread가 없지만, 이 앱은 FastAPI/Uvicorn/FastMCP의 `asyncio` 비동기 I/O로 많은 HTTP 요청을 동시에 대기·처리합니다. `uvicorn[standard]` 환경에서는 지원 OS에서 `uvloop`도 사용합니다. 단일 서버에서 캐시와 JSON 쓰기 일관성을 보장하기 위해 워커는 1개로 고정했고, `MCP_CONCURRENCY_LIMIT`(기본 1000)과 `backlog=2048`로 과부하 시 백프레셔를 적용합니다. 실제 폐쇄망 환경의 CPU/메모리와 요청 크기로 부하 테스트 후 이 제한을 조정하세요.

호출 이력은 메모리 링 버퍼에 먼저 남기고 `data/call_logs.jsonl`에 백그라운드 배치로 기록하므로, 로그 파일 쓰기가 MCP 응답을 지연시키지 않습니다. `MCP_LOG_MEMORY_LIMIT`(기본 10,000)과 `MCP_LOG_QUEUE_LIMIT`(기본 20,000)으로 메모리·큐 상한을 조정할 수 있습니다. 민감한 이름의 필드(`password`, `token`, `secret`, `authorization`, `api_key`)는 로그에서 마스킹됩니다.

JSON + 프로세스 메모리는 하나의 쓰기 인스턴스에 적합합니다. JSON 파일을 여러 프로세스/인스턴스가 공유해 쓰는 방식은 지원하지 않습니다. 향후 Tibero로 전환할 때는 `app/storage.py` 하단의 주석 처리된 `TiberoRegistryRepository` 골격을 사용해 `RegistryRepository` 구현체만 교체하면 됩니다. 서비스의 L1 캐시는 Tibero 조회보다 먼저 사용되고, DB 트랜잭션 성공 뒤에만 캐시를 다시 만듭니다.

`MCP_MAX_REQUEST_BYTES`(기본 1 MiB)로 큰 요청을 조기에 차단할 수 있습니다. 응답 압축과 liveness/readiness probe도 포함했습니다.

## 폐쇄망 설치

- `uv.lock`에는 전체 전이 의존성 버전과 아티팩트 해시가 고정되어 있습니다.
- `requirements.txt`는 내부 PyPI/휠 저장소에서 `pip`을 써야 하는 환경용 직접 의존성 목록입니다.
- `run-offline.sh`는 uv 캐시 또는 내부 인덱스에 필요한 CPython 3.13과 휠이 이미 준비된 폐쇄망에서 실행합니다.

```bash
chmod +x run-offline.sh
./run-offline.sh
```

인터넷이 연결된 반입 준비 환경에서는 대상 OS/CPU의 Python 3.13과 모든 잠긴 휠을 내부 패키지 저장소 또는 uv 캐시에 미리 채운 뒤 반입해야 합니다. 폐쇄망에서는 `uv sync --offline --frozen --no-dev`를 사용하므로 잠금 파일을 다시 해석하거나 인터넷에 연결하지 않습니다.

## API 요약

| 용도 | 경로 |
| --- | --- |
| 공개 MCP 목록 | `GET /api/mcps` |
| MCP 등록 | `POST /api/mcps` |
| Tool별 HTTP 목 호출 | `POST /api/mock/mcps/{slug}/tools/{tool_name}/invoke` |
| Tool 호출 이력 조회 | `GET /api/logs?date_from=YYYY-MM-DD&date_to=YYYY-MM-DD&mcp_id={id}` |
| MCP Streamable HTTP | `POST /mcp/{slug}` |
| 시나리오 목록/등록 | `GET`, `POST /api/scenarios` |
| 관리자 카테고리 추가 | `POST /api/admin/categories` |

MCP 등록자만 원래의 이름과 사번을 포함해 수정·삭제할 수 있습니다. MCP를 삭제하면 연결된 시나리오에서는 해당 MCP가 자동 제거됩니다.