mcp-api-server
by istanadodan
README.md
# MCP API 서버
FastAPI 및 FastMCP 기반의 Model Context Protocol (MCP) 서버로, YOLOv8 이미지 분석과 PostgreSQL 데이터베이스 통합을 지원합니다.
## 프로젝트 개요
이 프로젝트는 Claude와 같은 AI 에이전트가 이미지를 분석하고 결과를 데이터베이스에 저장할 수 있도록 MCP 프로토콜을 통해 제공합니다.
**주요 기능:**
- **YOLOv8 이미지 분석** - nano/small/medium 모델 지원 (빠른 추론)
- **감지된 객체 자동 처리** - 크롭 및 base64 인코딩
- **PostgreSQL 데이터 저장** - asyncpg 기반 비동기 DB 연결
- **4-Layer 아키텍처** - Presenter → Service → Core → Utility 레이어 구분
- **MCP 3가지 전송 방식** - StdIO (Claude Desktop), HTTP/JSON-RPC, WebSocket
---
## 시스템 요구사항
- **Python:** 3.13.3 이상
- **패키지 매니저:** `uv` ([설치 가이드](https://docs.astral.sh/uv/getting-started/installation/))
- **메모리:** 2GB 이상 (YOLOv8 모델 로드용)
- **데이터베이스:** PostgreSQL 12+ (pgvector 확장 필수)
---
## 빠른 시작
### 1단계: 의존성 설치
```bash
# 프로젝트 루트에서 실행
uv sync
```
### 2단계: 데이터베이스 설정
PostgreSQL 데이터베이스를 생성하고 `.env` 파일에 접속 정보를 설정합니다:
```bash
# .env 파일 생성
cat > .env << EOF
# PostgreSQL 데이터베이스 설정
DB_HOST=localhost
DB_PORT=5432
DB_NAME=mcp_db
DB_USER=postgres
DB_PASSWORD=your_password
# YOLOv8 모델 설정
YOLO_MODEL=yolov8n.pt
YOLO_HOME=./src/mcp_server/models
YOLO_CONF_THRESHOLD=0.5
# MCP 서버 설정
MCP_SERVER_NAME=mcp-api-server
LOG_LEVEL=INFO
EOF
```
**설정 항목 설명:**
| 항목 | 기본값 | 설명 |
| --------------------- | ------------------------------- | ---------------------------------------------- |
| `DB_HOST` | `localhost` | PostgreSQL 호스트 |
| `DB_PORT` | `5432` | PostgreSQL 포트 |
| `DB_NAME` | `mcp_db` | 데이터베이스명 |
| `DB_USER` | `postgres` | DB 사용자명 |
| `DB_PASSWORD` | - | DB 암호 |
| `YOLO_MODEL` | `yolov8n.pt` | YOLOv8 모델 (nano/s/m/l/x) |
| `YOLO_HOME` | `./src/mcp_server/models` | 모델 다운로드 디렉토리 |
| `YOLO_CONF_THRESHOLD` | `0.5` | 감지 신뢰도 임계값 (0~1) |
| `MCP_SERVER_NAME` | `mcp-api-server` | MCP 서버 이름 |
| `LOG_LEVEL` | `INFO` | 로그 레벨 (DEBUG/INFO/WARNING) |
**YOLOv8 모델 선택 가이드:**
| 모델 | 추론시간 | 메모리 | 정확도 | 추천 상황 |
| ---------- | -------- | ------- | ------ | -------------- |
| yolov8n.pt | ~3ms | <100MB | 낮음 | 빠른 응답 필요 |
| yolov8s.pt | ~10ms | 200MB | 중간 | ✅ 권장 |
| yolov8m.pt | ~20ms | 400MB | 높음 | 정확도 중시 |
### 3단계: 서버 실행
**MCP StdIO 서버 (Claude Desktop 연결용):**
```bash
# FastMCP 방식 (권장, 간단함)
uv run python -m mcp_server
# 또는 MCP SDK 방식 (더 많은 기능)
MCP_SERVER_TYPE=sdk uv run python -m mcp_server
```
**FastAPI HTTP/WebSocket 서버 (원격 클라이언트용):**
```bash
# 개발 모드 (자동 리로드 활성화)
uv run uvicorn src.mcp_server.main:app --reload
# 프로덕션 모드 (4개 워커)
uv run uvicorn src.mcp_server.main:app --workers 4
```
**서버 시작 확인:**
- FastMCP StdIO: 터미널에서 "Starting FastMCP server" 로그 메시지 확인
- FastAPI HTTP: 브라우저에서 `http://localhost:8000/health` 접속 → `{"status": "ok"}`
---
## 프로젝트 구조
```
mcp-api-server/
│
├── .git/ # Git 저장소
├── .python-version # Python 3.13.3
├── .env.example # 환경변수 템플릿
├── .gitignore
│
├── pyproject.toml # uv 프로젝트 설정
├── uv.lock # 의존성 잠금 파일
│
├── README.md # 이 파일
├── CLAUDE.md # 개발 규약
├── NOTION_USAGE_GUIDE.md # Notion 작업 문서 규칙
├── ARCHITECTURE.md # 아키텍처 설명
│
├── src/
│ └── mcp_server/ # 메인 패키지
│ ├── __main__.py # MCP StdIO 진입점
│ ├── main.py # FastAPI HTTP/WS 진입점
│ ├── fastmcp_server.py # FastMCP 서버 구현
│ ├── server.py # MCP 서버 인스턴스 (SDK용)
│ │
│ ├── commons/ # 공유 유틸리티 (infrastructure)
│ │ ├── config.py # Pydantic Settings (환경변수)
│ │ ├── database.py # SQLAlchemy AsyncEngine + asyncpg
│ │ ├── logging.py # 로깅 설정
│ │ └── exceptions.py # 커스텀 예외
│ │
│ ├── core/ # 도메인 모델 (domain layer)
│ │ ├── yolo_model.py # YOLOv8 lazy loading (스레드 안전)
│ │ └── embedding_client.py # 외부 API 클라이언트
│ │
│ ├── schemas/ # 데이터 모델 (Pydantic)
│ │ ├── response.py # 응답 모델 (Detection, AnalysisResult, ImageRecordResponse)
│ │ └── img_model.py # DB ORM 모델 (ImageRecord)
│ │
│ ├── services/ # 비즈니스 로직 (service layer)
│ │ ├── image_analyzer_service.py # YOLOv8 분석 로직
│ │ ├── image_query_service.py # DB 조회 로직
│ │ └── repository.py # DB 저장 로직
│ │
│ ├── tools/ # MCP Tool 구현 (presenter layer)
│ │ ├── __init__.py
│ │ └── image_analysis.py # analyze_image tool
│ │
│ ├── resources/ # MCP Resource (선택)
│ │ └── __init__.py
│ │
│ ├── prompts/ # MCP Prompt (선택)
│ │ └── __init__.py
│ │
│ └── utils/ # 공유 유틸리티
│ ├── suppress_output.py # 출력 억제 데코레이터
│ └── image_utils.py # 이미지 처리 헬퍼
│
├── tests/ # 테스트 스위트
│ ├── conftest.py # pytest fixtures
│ ├── test_models.py # Pydantic 모델 테스트
│ └── test_*.py # 기능별 테스트
│
└── examples/ (선택) # 클라이언트 예제
├── http_client.py # HTTP JSON-RPC 클라이언트
└── websocket_client.py # WebSocket 클라이언트
```
---
## 아키텍처 (4 Layers)
```
┌──────────────────────────────────────────────────────────┐
│ Presenter Layer (MCP Tools) │
│ - tools/image_analysis.py │
│ - MCP Protocol 변환 (JSON-RPC 2.0) │
└──────────────────┬───────────────────────────────────────┘
│
┌──────────────────▼───────────────────────────────────────┐
│ Service Layer (비즈니스 로직) │
│ - services/image_analyzer_service.py │
│ - services/image_query_service.py │
│ - @atransactional 데코레이터로 DB 트랜잭션 관리 │
└──────────────────┬───────────────────────────────────────┘
│
┌──────────────────▼───────────────────────────────────────┐
│ Core Layer (도메인 모델) │
│ - core/yolo_model.py (double-checked locking) │
│ - core/embedding_client.py │
│ - schemas/img_model.py (SQLAlchemy ORM) │
└──────────────────┬───────────────────────────────────────┘
│
┌──────────────────▼───────────────────────────────────────┐
│ Utility Layer (공유 인프라) │
│ - commons/database.py (AsyncEngine + asyncpg) │
│ - commons/config.py (환경변수) │
│ - utils/suppress_output.py (출력 억제) │
└──────────────────────────────────────────────────────────┘
```
**의존성 흐름:** Presenter → Service → Core → Utility (역방향 금지)
---
## 주요 기능 설명
### `analyze_image` MCP Tool
이미지 파일 경로를 입력받아 YOLOv8로 객체를 감지하고 결과를 저장합니다.
**입력 파라미터:**
| 파라미터 | 타입 | 필수 | 설명 |
| ---------------- | ------ | ---- | ----------------------------- |
| `image_path` | string | ✅ | 이미지 파일 절대 경로 |
| `conf_threshold` | float | ❌ | 감지 임계값 (0~1, 기본: 0.5) |
**반환 결과 (AnalysisResult):**
```json
{
"image_path": "/path/to/image.jpg",
"detections": [
{
"class_name": "person",
"confidence": 0.91,
"bbox": [100.0, 50.0, 200.0, 300.0]
}
],
"total_objects": 1
}
```
**처리 흐름:**
1. **입력 검증** - 파일 존재 확인
2. **이미지 전처리** - PIL로 로드, RGB 변환, 1024×1024 리사이징
3. **객체 감지** - YOLOv8 추론 (asyncio.to_thread 사용)
4. **DB 저장** - ImageRecord 생성 및 PostgreSQL 저장 (@atransactional)
5. **결과 반환** - Pydantic AnalysisResult 모델
**주요 특징:**
- **비동기 처리** - asyncio.to_thread()로 블로킹 작업 분리
- **자동 리사이징** - 1024×1024으로 일관된 처리 품질 보장
- **에러 처리** - DB 연결 실패 시 예외 발생, 로그 기록
- **타입 안정성** - Pydantic 모델로 검증
---
## 데이터베이스 구성
### PostgreSQL + asyncpg 설정
```bash
# PostgreSQL 설치 (Windows)
# https://www.postgresql.org/download/windows/
# 데이터베이스 생성
psql -U postgres -c "CREATE DATABASE mcp_db;"
```
### 데이터베이스 테이블 (자동 생성)
서버 첫 실행 시 `init_async_db()` 함수가 아래 테이블을 자동으로 생성합니다:
```sql
CREATE TABLE image_records (
id SERIAL PRIMARY KEY,
path VARCHAR NOT NULL UNIQUE,
category VARCHAR NOT NULL,
confidence FLOAT NOT NULL,
description TEXT,
objects JSONB,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
```
### 비동기 데이터베이스 접근
모든 DB 접근은 `@atransactional` 데코레이터를 사용합니다:
```python
from mcp_server.commons.database import atransactional, AsyncSession
from mcp_server.schemas.response import AnalysisResult
@atransactional
async def save_analysis(result: AnalysisResult, db: AsyncSession):
"""데이터베이스에 분석 결과 저장"""
from mcp_server.schemas.img_model import ImageRecord
record = ImageRecord(
path=result.image_path,
category="detected_object",
confidence=result.detections[0].confidence if result.detections else 0.0,
)
db.add(record)
# 트랜잭션 자동 커밋 (예외 발생 시 자동 롤백)
```
**특징:**
- **asyncpg 기반** - PostgreSQL 비동기 드라이버
- **Lazy initialization** - FastMCP 루프에서만 engine 생성
- **자동 트랜잭션** - 데코레이터로 commit/rollback 자동 관리
- **세션 주입** - 함수 파라미터에 자동으로 db 제공
---
## API 엔드포인트
### 헬스 체크 및 서버 정보
```bash
# 헬스 체크
GET /health
→ {"status": "ok"}
# 서버 정보
GET /info
```
### MCP HTTP/JSON-RPC (네트워크 기반)
**엔드포인트:** `POST /mcp/messages`
```python
import httpx
import asyncio
async def call_tool():
async with httpx.AsyncClient() as client:
response = await client.post(
"http://localhost:8000/mcp/messages",
json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "analyze_image",
"arguments": {
"image_path": "/absolute/path/to/image.jpg",
"conf_threshold": 0.5
}
}
}
)
print(response.json())
asyncio.run(call_tool())
```
### MCP WebSocket 양방향 통신
**엔드포인트:** `WS /mcp/ws`
```python
import asyncio
import json
import websockets
async def ws_client():
async with websockets.connect("ws://localhost:8000/mcp/ws") as ws:
await ws.send(json.dumps({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {}
}))
response = await ws.recv()
print(f"Response: {response}")
asyncio.run(ws_client())
```
### MCP StdIO (Claude Desktop)
**Claude Desktop 설정:**
macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
Windows: `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"mcp-api-server": {
"command": "uv",
"args": ["run", "python", "-m", "mcp_server"],
"cwd": "/absolute/path/to/mcp-api-server"
}
}
}
```
---
## 개발 명령어
### 테스트 실행
```bash
# 모든 테스트 실행
uv run pytest
# 상세 출력
uv run pytest -v
# 특정 테스트 실행
uv run pytest tests/test_models.py -v
# 커버리지 리포트
uv run pytest --cov=src
```
### 코드 린트 및 포맷팅
```bash
# 린트 검사
uv run ruff check .
# 자동 포맷팅
uv run ruff format .
# 린트 + 포맷 함께
uv run ruff check . && uv run ruff format .
```
### 타입 검사
```bash
# VS Code Pylance strict mode 사용 (IDE에서 설정)
# .vscode/settings.json:
{
"python.analysis.typeCheckingMode": "strict"
}
```
---
## 트러블슈팅
### YOLOv8 모델 자동 다운로드 실패
```bash
# 수동 다운로드
uv run python -c "from ultralytics import YOLO; YOLO('yolov8n.pt')"
# 또는 서버 실행 (자동 다운로드)
uv run uvicorn src.mcp_server.main:app
```
**모델 저장 위치:** `./src/mcp_server/models/` (`.gitignore` 제외)
### PostgreSQL 연결 오류
```bash
# DB 접속 가능 확인
psql -h localhost -U postgres -d mcp_db -c "SELECT 1;"
# 방화벽 설정 확인 (Windows)
# → Defender 방화벽에서 PostgreSQL 포트 5432 허용
```
### asyncpg 루프 바인딩 에러
에러: `'NoneType' object has no attribute 'send'`
**원인:** asyncpg 커넥션이 다른 event loop에 바인딩됨
**해결:**
- `init_async_db()`는 별도 loop에서 실행 (스키마만 초기화)
- 실제 engine은 FastMCP/FastAPI loop에서 lazy 생성
- 자세한 설명은 [ARCHITECTURE.md](ARCHITECTURE.md) 참고
### 메모리 부족
```bash
# 더 작은 모델 사용
echo "YOLO_MODEL=yolov8n.pt" >> .env
# 또는 명령어로 실행
YOLO_MODEL=yolov8s.pt uv run uvicorn src.mcp_server.main:app
```
---
## 개발 규약
모든 개발 규약은 [CLAUDE.md](CLAUDE.md)에 자세히 기술되어 있습니다.
**주요 규약:**
- **언어:** Python 3.13.3 이상
- **타입 힌팅:** Pylance strict mode 필수 (모든 함수/파라미터)
- **코드 스타일:** Ruff (line-length: 100)
- **데이터 모델:** Pydantic BaseModel + SQLAlchemy ORM
- **커밋 메시지:** Conventional Commits
- **테스트:** pytest + pytest-asyncio
- **아키텍처:** 4-Layer (Presenter → Service → Core → Utility)
---
## Git 워크플로우
### 새 기능 추가
```bash
git checkout -b feature/기능-이름
# ... 작업 ...
git add .
git commit -m "feat(scope): 기능 설명"
git push -u origin feature/기능-이름
```
### 버그 수정
```bash
git checkout -b fix/버그-이름
# ... 수정 ...
git commit -m "fix(scope): 버그 설명"
git push -u origin fix/버그-이름
```
---
## 성능 최적화
### YOLOv8 모델 선택
| 모델 | 추론시간 | 메모리 | 정확도 | 추천 |
| ---------- | -------- | ------ | ------ | ----------- |
| yolov8n.pt | ~3ms | <100MB | 낮음 | 빠른 응답 |
| yolov8s.pt | ~10ms | 200MB | 중간 | ✅ 권장 |
| yolov8m.pt | ~20ms | 400MB | 높음 | 정확도 중시 |
| yolov8l.pt | ~40ms | 800MB | 매우높음 | 매우 정확 |
### 비동기 배치 처리
```python
from mcp_server.services.image_analyzer_service import ImageAnalyzerService
service = ImageAnalyzerService()
# 여러 이미지를 동시에 분석
results = await service.analyze_images_abatch([
"/path/to/image1.jpg",
"/path/to/image2.jpg",
"/path/to/image3.jpg",
])
```
---
## 라이선스
MIT License
---
## 참고 자료
- [MCP 공식 문서](https://modelcontextprotocol.io/)
- [FastAPI 문서](https://fastapi.tiangolo.com/)
- [FastMCP 문서](https://github.com/jqlang/fastmcp)
- [YOLOv8 문서](https://docs.ultralytics.com/)
- [SQLAlchemy 비동기](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html)
- [asyncpg 문서](https://magicstack.github.io/asyncpg/)
- [Pydantic 문서](https://docs.pydantic.dev/)
- [uv 패키지 매니저](https://docs.astral.sh/uv/)
더 자세한 개발 정보는 [CLAUDE.md](CLAUDE.md)를 참고하세요.
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues