readonly-db-mcp
by polaris428
README.md
# readonly-db-mcp
SQL Server 데이터베이스를 읽기 전용으로 조회하는 MCP 서버입니다.
멀티 테넌트 프로젝트에서 매번 `.env`를 바꾸지 않도록, 자주 쓰는 DB 접속 환경은 `db_env` 프리셋으로 등록하고 실제 조회할 DB는 tool 호출 시 `database_name`으로 지정합니다.
## 제공 Tool
- `list_tables(schema_name="dbo", db_env=None, db_host=None, db_port=None, database_name=None)`
- `describe_table(schema_name, table_name, db_env=None, db_host=None, db_port=None, database_name=None)`
- `query_table(request)`
`query_table`의 `request`에는 다음 DB 선택 필드를 넣을 수 있습니다.
- `db_env`: `.env`에 등록한 접속 프리셋 이름. 예: `local`, `qa`, `qa_system`
- `database_name`: 실제 조회할 SQL Server database 이름. 예: `c_test`
- `db_host`, `db_port`: 프리셋 없이 직접 host/port를 지정해야 할 때만 사용
## 안전 정책
`query_table`은 Raw SQL을 받지 않습니다. 테이블/컬럼을 실제 스키마와 비교하고 다음을 강제합니다.
- `SELECT`만 실행
- 단일 테이블만 조회
- `WITH (NOLOCK)` 적용
- 최대 행 수 제한
- 파라미터 바인딩
- 쿼리 타임아웃
- 한글 컬럼명 지원
데이터 조회 전 내부적으로 테이블 컬럼 목록을 조회해서 요청 컬럼, 필터 컬럼, 정렬 컬럼이 실제로 존재하는지 검증합니다.
## 준비
- Python 3.11 이상
- Microsoft ODBC Driver 18 for SQL Server
- 조회 대상 DB에 읽기 권한이 있는 계정
Docker로 실행하면 ODBC 드라이버는 이미지 안에 포함됩니다.
## 설치
로컬 Python 실행:
```bash
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -U pip
pip install -e ".[dev]"
```
Docker 실행:
```bash
docker build -t readonly-db-mcp:local .
```
## 환경 변수
개인별 최초 셋업은 스크립트로 진행합니다.
```bash
python3 scripts/setup_mcp.py
```
이 스크립트는 다음 작업을 합니다.
- 개인 설정 파일 `.env.local` 생성
- Docker 이미지 `readonly-db-mcp:local` 빌드
- Codex MCP 등록 갱신
- Claude MCP 등록 갱신
`.env.local`은 개인 DB 계정/비밀번호를 담는 파일이며 git에 올리지 않습니다. 공용 예시는 `.env.example`에만 둡니다.
기본 구조:
```dotenv
DB_USER=readonly_user
DB_PASSWORD=change-me
DB_DEFAULT_ENV=local
DB_LOCAL_HOST=db-tenant
DB_LOCAL_PORT=14332
DB_QA_HOST=10.1.1.49
DB_QA_PORT=14332
DB_QA_USER=readonly_user
DB_QA_PASSWORD=change-me
DB_QA_SYSTEM_HOST=host.docker.internal
DB_QA_SYSTEM_PORT=14331
DB_QA_SYSTEM_USER=readonly_user
DB_QA_SYSTEM_PASSWORD=change-me
DB_DRIVER=ODBC Driver 18 for SQL Server
DB_ENCRYPT=yes
DB_TRUST_SERVER_CERTIFICATE=yes
DB_QUERY_TIMEOUT_SECONDS=3
DB_MAX_ROWS=100
```
현재 프리셋 의미:
- `local`: 로컬 tenant DB. 기본값은 `db-tenant:14332`
- `qa`: QA tenant DB. 기본값은 `10.1.1.49:14332`
- `qa_system`: QA system DB. Docker 안에서는 `host.docker.internal:14331`로 접근
프리셋 규칙:
- `db_env=qa`는 `DB_QA_HOST`, `DB_QA_PORT`, `DB_QA_USER`, `DB_QA_PASSWORD`를 사용합니다.
- `db_env=qa_system`은 `DB_QA_SYSTEM_HOST`, `DB_QA_SYSTEM_PORT`, `DB_QA_SYSTEM_USER`, `DB_QA_SYSTEM_PASSWORD`를 사용합니다.
- `db_host`/`db_port`를 tool 호출에 직접 넘기면 프리셋보다 우선합니다.
- `DB_CONNECTION_STRING`을 설정하면 모든 분리 변수와 tool 호출 인자보다 우선합니다.
비밀번호에 `;` 또는 `}`가 있어도 자동으로 ODBC 형식에 맞게 감쌉니다.
## 직접 실행
```bash
readonly-db-mcp
```
stdio MCP 서버이므로 실행 후 화면이 멈춘 것처럼 보이는 것이 정상입니다.
Docker로 직접 실행:
```bash
docker run --rm -i \
--network sellmate-dockerize_default \
--add-host host.docker.internal:host-gateway \
--env-file /home/polaris/readonly-db-mcp/.env.local \
readonly-db-mcp:local
```
## Codex MCP 등록 예시
`/home/polaris/.codex/config.toml`:
```toml
[mcp_servers.readonly_db]
command = "docker"
args = ["run", "--rm", "-i", "--network", "sellmate-dockerize_default", "--add-host", "host.docker.internal:host-gateway", "--env-file", "/home/polaris/readonly-db-mcp/.env.local", "readonly-db-mcp:local"]
```
## Claude MCP 등록 예시
```bash
claude mcp add -s user readonly_db -- \
docker run --rm -i \
--network sellmate-dockerize_default \
--add-host host.docker.internal:host-gateway \
--env-file /home/polaris/readonly-db-mcp/.env.local \
readonly-db-mcp:local
```
등록 확인:
```bash
claude mcp get readonly_db
```
stdio 서버 특성상 헬스체크가 `Failed to connect`로 보일 수 있습니다. 실제 Claude 세션에서 tool이 보이고 호출되면 정상입니다.
## 사용 예시
Claude/Codex에 자연어로 요청:
```text
readonly_db MCP로 qa 환경의 c_yongma DB에서 발주정보 테이블 구조 조회해줘.
```
명시적 tool 인자 기준:
```json
{
"schema_name": "dbo",
"table_name": "발주정보",
"db_env": "qa",
"database_name": "c_yongma"
}
```
`query_table` 요청 예시:
```json
{
"request": {
"db_env": "qa",
"database_name": "c_yongma",
"schema_name": "dbo",
"table_name": "발주정보",
"columns": ["일련번호", "SEQ코드"],
"filters": [
{
"column": "판매처주문번호",
"operator": "eq",
"value": "2026071610"
}
],
"order_by": [
{
"column": "일련번호",
"direction": "desc"
}
],
"limit": 50
}
}
```
지원 연산자:
- `eq`, `neq`
- `gt`, `gte`, `lt`, `lte`
- `in`
- `like`
- `is_null`, `not_null`
## 테스트
```bash
pytest
```
## 현재 범위
포함:
- SQL Server 읽기 전용 조회
- 환경 프리셋 기반 DB 접속 선택
- 요청별 database 선택
- 테이블 목록 및 컬럼 구조 조회
- 단일 테이블 데이터 조회
- 한글 테이블명/컬럼명 조회
제외:
- system DB에서 tenant 자동 탐색
- 사용자 인증 및 권한 승인
- JOIN
- Raw SQL
- INSERT/UPDATE/DELETE
- 웹 API
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues