db-mcp
by NohYusung
README.md
<div align="center">
<h1>db-mcp</h1>
<p><strong>설정 파일 하나로 연결 대상을 계속 확장하는<br />MySQL·MariaDB 읽기 전용 MCP 서버</strong></p>
<p>
<img src="https://img.shields.io/badge/version-0.4.0-5A67D8" alt="Version 0.4.0" />
<img src="https://img.shields.io/badge/TypeScript-strict-3178C6?logo=typescript&logoColor=white" alt="TypeScript strict" />
<img src="https://img.shields.io/badge/Node.js-20+-339933?logo=nodedotjs&logoColor=white" alt="Node.js 20+" />
<img src="https://img.shields.io/badge/MCP-stdio-111111" alt="MCP stdio" />
<img src="https://img.shields.io/badge/MySQL%20%2F%20MariaDB-read--only-003545?logo=mariadb&logoColor=white" alt="MySQL and MariaDB read-only" />
</p>
</div>
> [!NOTE]
> target을 추가하기 위해 소스 코드를 수정할 필요가 없습니다. 로컬 웹 대시보드에서 연결을 관리하거나 YAML과 `.env`를 직접 수정한 뒤 서버를 다시 시작하면 됩니다.
## Why db-mcp?
- 여러 MySQL·MariaDB 환경을 하나의 MCP 등록으로 관리합니다.
- target 개수가 코드에 고정되지 않아 필요한 만큼 계속 추가할 수 있습니다.
- 연결 정보는 `.env`, 공개 가능한 routing 정책은 YAML로 분리합니다.
- Direct와 SSH bastion 연결을 같은 9개 도구로 다룹니다.
- target과 schema를 명시적으로 선택하고, 허용 범위를 연결 전에 검증합니다.
- 쓰기 SQL과 과도한 결과 조회를 애플리케이션과 DB 권한 양쪽에서 차단할 수 있습니다.
## 동작 구조
```mermaid
flowchart LR
Admin["Local Web Dashboard"] -->|"atomic update"| Config
Admin -->|"masked secrets"| Env
Client["Codex / MCP Client"] -->|"stdio"| MCP["db-mcp"]
Config["db-mcp.config.yaml"] --> Registry["Dynamic Target Registry"]
Env[".env / Process Env"] --> Registry
MCP --> Tools["9 MCP Tools"]
Tools --> Registry
Tools --> Policy["Schema · SQL Policy"]
Policy --> Router["Connection Router"]
Router -->|"direct"| Direct["MySQL / MariaDB"]
Router --> Lock["Per-target Lock"]
Lock --> Tunnel["Ephemeral SSH Tunnel"]
Tunnel --> Remote["Remote MySQL / MariaDB"]
```
- 서버 시작 시 YAML을 검증하고 runtime target registry를 만듭니다.
- MCP 입력의 `target`은 registry에서 실행 시점에 검증됩니다.
- `list_targets`는 현재 사용할 수 있는 target과 공개 메타데이터를 반환합니다.
- 비밀번호와 환경 변수 이름은 MCP 응답에 포함하지 않습니다.
## 주요 기능
| 기능 | 설명 |
| --- | --- |
| Dynamic Registry | YAML만 수정해 target을 추가·삭제·변경합니다. |
| Schema Allowlist | target별로 접근 가능한 schema를 명시합니다. 기본값은 deny입니다. |
| Read-only SQL | `SELECT`와 `WITH ... SELECT`만 허용합니다. |
| Bounded Results | SQL의 `LIMIT`을 금지하고 bounded tool argument를 강제합니다. |
| Direct / SSH | 직접 연결과 SSH bastion forwarding을 함께 지원합니다. |
| Automatic Port | SSH local port를 생략하면 요청마다 사용 가능한 포트를 할당합니다. |
| Safe Lifecycle | 요청 종료 시 DB 연결과 SSH process를 성공·실패와 관계없이 정리합니다. |
| Local Dashboard | 연결 현황, 추가·수정·삭제, 설정 검증과 실제 연결 테스트를 웹에서 수행합니다. |
| Config CLI | 초기화, 설정 검증, target 조회와 실제 연결 확인 명령을 제공합니다. |
## 빠른 시작
### 요구 사항
- Node.js 20 이상
- npm
- MySQL 또는 MariaDB 읽기 전용 계정
- SSH target을 사용할 경우 OpenSSH와 key 기반 인증
### 1. 설치
```bash
git clone https://github.com/NohYusung/db-mcp.git
cd db-mcp
npm ci
```
### 2. 로컬 설정 생성
```bash
node dist/cli.js init
cp .env.example .env
chmod 600 .env
```
- `init`은 기존 파일을 덮어쓰지 않습니다.
- `db-mcp.config.yaml`과 `.env`는 Git에서 제외됩니다.
- 공개 예제에는 하나의 direct target과 주석 처리된 SSH 예제가 들어 있습니다.
### 3. 대시보드 실행
```bash
npm run dashboard
```
- 브라우저에서 `http://127.0.0.1:7331`을 열면 현재 target을 한눈에 확인할 수 있습니다.
- Direct와 SSH target을 추가·수정·삭제하고, schema allowlist와 실제 연결을 검증할 수 있습니다.
- 대시보드는 loopback에서만 실행되며 DB password 원문을 브라우저로 반환하지 않습니다.
- 포트를 바꾸려면 `node dist/cli.js dashboard --port 7441`처럼 실행합니다.
### 4. 연결값과 Schema 직접 설정
`db-mcp.config.yaml`:
```yaml
version: 1
targets:
local-app:
description: Local application database
driver: mysql
mode: direct
defaultSchema: app
allowedSchemas:
- app
connection:
hostEnv: APP_DB_HOST
portEnv: APP_DB_PORT
userEnv: APP_DB_USER
passwordEnv: APP_DB_PASSWORD
```
`.env`:
```dotenv
APP_DB_HOST=127.0.0.1
APP_DB_PORT=3306
APP_DB_USER=readonly_user
APP_DB_PASSWORD=replace_me
```
### 5. 설정 검증
```bash
node dist/cli.js validate
node dist/cli.js list-targets
node dist/cli.js test-connection local-app
```
### 6. Codex 등록
```bash
codex mcp add db-mcp -- /absolute/path/to/db-mcp/scripts/start.sh
codex mcp get db-mcp
```
- `scripts/start.sh`는 저장소의 ignored config와 `.env`를 명시적으로 사용합니다.
- config를 변경하면 Codex의 MCP process를 다시 시작해야 합니다.
## Target 추가
대시보드의 `연결 추가`를 사용하면 target 정책과 환경 변수 참조를 함께 생성합니다. 수동으로 관리하려면 다음 블록을 `targets` 아래에 추가합니다.
```yaml
analytics-prod:
description: Analytics database through an SSH bastion
driver: mysql
mode: ssh
defaultSchema: analytics
allowedSchemas:
- analytics
connection:
hostEnv: ANALYTICS_DB_HOST
portEnv: ANALYTICS_DB_PORT
userEnv: ANALYTICS_DB_USER
passwordEnv: ANALYTICS_DB_PASSWORD
ssh:
bastionHostEnv: ANALYTICS_SSH_BASTION_HOST
bastionUserEnv: ANALYTICS_SSH_BASTION_USER
identityFileEnv: ANALYTICS_SSH_IDENTITY_FILE
```
그리고 참조하는 값을 `.env`에 추가합니다.
```dotenv
ANALYTICS_DB_HOST=db.internal
ANALYTICS_DB_PORT=3306
ANALYTICS_DB_USER=readonly_user
ANALYTICS_DB_PASSWORD=replace_me
ANALYTICS_SSH_BASTION_HOST=bastion.example.com
ANALYTICS_SSH_BASTION_USER=ec2-user
ANALYTICS_SSH_IDENTITY_FILE=/absolute/path/to/id_ed25519
```
```bash
node dist/cli.js validate
node dist/cli.js test-connection analytics-prod
```
- target 개수에는 애플리케이션의 고정 상한이 없습니다.
- target ID는 문자나 숫자로 시작하고 문자, 숫자, `.`, `_`, `-`를 사용할 수 있습니다.
- SSH local port를 지정하지 않으면 사용 가능한 loopback port를 자동 할당합니다.
- 설정 전체 내용은 [Configuration reference](./docs/configuration.md)에서 확인할 수 있습니다.
## CLI
| 명령 | 설명 |
| --- | --- |
| `db-mcp init` | 예제 config와 환경 변수 템플릿을 생성합니다. |
| `db-mcp dashboard` | `127.0.0.1`에서 로컬 연결 관리 대시보드를 실행합니다. |
| `db-mcp validate` | YAML 구조와 모든 target의 환경 변수 참조를 검증합니다. |
| `db-mcp list-targets` | 자격증명을 제외한 runtime target 목록을 JSON으로 출력합니다. |
| `db-mcp test-connection <target>` | 선택 target에서 `SELECT 1`을 실행해 연결을 확인합니다. |
| `db-mcp serve` | stdio MCP 서버를 실행합니다. |
저장소에서 직접 실행할 때는 `npm ci`에서 생성된 `dist/cli.js`를 사용합니다.
```bash
node dist/cli.js --help
node dist/cli.js dashboard --port 7441
node dist/cli.js validate --config ./custom.yaml --env ./custom.env
```
다음 환경 변수로 기본 경로를 변경할 수도 있습니다.
```dotenv
DB_MCP_CONFIG=/absolute/path/to/db-mcp.config.yaml
DB_MCP_ENV=/absolute/path/to/.env
DB_MCP_DASHBOARD_PORT=7331
```
## Web Dashboard
### 연결 현황 대시보드
<p align="center">
<img src="./docs/assets/dashboard-overview.png" alt="db-mcp 연결 현황 대시보드" width="100%" />
</p>
<p align="center"><sub>등록된 Direct·SSH target과 schema 허용 범위, 설정 상태를 한 화면에서 확인합니다.</sub></p>
### 신규 DB 연결 등록
<p align="center">
<img src="./docs/assets/dashboard-add-connection.png" alt="db-mcp 신규 DB 연결 등록 화면" width="860" />
</p>
<p align="center"><sub>대시보드에서 연결 정책과 DB endpoint를 입력해 새로운 target을 등록합니다.</sub></p>
- 현재 target 수, 설정 완료 수, Direct·SSH 분포를 요약합니다.
- host, port, read-only user, SSH bastion 경로와 schema allowlist를 카드로 표시합니다.
- 검색과 연결 유형·오류 상태 필터를 제공합니다.
- target 저장 시 YAML의 해당 항목과 `.env`의 필요한 키만 atomic하게 갱신합니다.
- 기존 password는 수정 화면에 표시하지 않으며 빈 값으로 저장하면 기존 값을 유지합니다.
- target을 삭제해도 `.env`의 자격증명은 데이터 손실 방지를 위해 자동 삭제하지 않습니다.
- 변경 내용은 다음 MCP process 시작부터 반영되므로 실행 중인 Codex MCP를 재시작해야 합니다.
> [!WARNING]
> 대시보드를 reverse proxy로 외부에 공개하지 마세요. 서버는 loopback bind와 Host 검증, 난수 세션 token, same-origin 변경 검증과 CSP를 적용하지만 로컬 관리 도구를 전제로 설계되어 있습니다.
## MCP Tools
| Tool | 필수 입력 | 선택 입력 | 설명 |
| --- | --- | --- | --- |
| `list_targets` | — | — | 현재 registry의 target과 공개 연결 정책을 조회합니다. |
| `list_schemas` | `target` | — | allowlist에 포함된 visible schema를 반환합니다. |
| `list_tables` | `target` | `schema` | 테이블·뷰 목록과 객체 유형을 반환합니다. |
| `describe_table` | `target`, `table` | `schema` | 컬럼 타입, nullable, key와 extra 정보를 조회합니다. |
| `show_create_definition` | `target`, `table` | `schema` | 테이블 또는 뷰의 `SHOW CREATE` 결과를 반환합니다. |
| `show_indexes` | `target`, `table` | `schema` | 인덱스 컬럼, 순서, cardinality와 유형을 조회합니다. |
| `show_foreign_keys` | `target`, `table` | `schema` | 외래 키와 update/delete rule을 조회합니다. |
| `run_select_query` | `target`, `query` | `limit` | 정책을 통과한 읽기 전용 SQL을 실행합니다. |
| `explain_query` | `target`, `query` | — | 검증된 쿼리의 `EXPLAIN` 계획을 반환합니다. |
## 호출 예시
### Target 탐색
```json
{
"tool": "list_targets",
"arguments": {}
}
```
### Table 구조 확인
```json
{
"tool": "describe_table",
"arguments": {
"target": "local-app",
"schema": "app",
"table": "users"
}
}
```
### 읽기 전용 조회
```json
{
"tool": "run_select_query",
"arguments": {
"target": "local-app",
"query": "SELECT id, email FROM app.users ORDER BY id DESC",
"limit": 25
}
}
```
### 실행 계획 확인
```json
{
"tool": "explain_query",
"arguments": {
"target": "local-app",
"query": "SELECT id FROM app.users WHERE email = 'user@example.com'"
}
}
```
## SQL 안전 정책
| 정책 | 동작 |
| --- | --- |
| Query Type | `SELECT` 또는 `WITH ... SELECT`만 허용합니다. |
| Single Statement | 다중 문장과 끝의 세미콜론을 거부합니다. |
| Write / DDL | 쓰기, DDL, grant, procedure, file output을 차단합니다. |
| Locking Read | `FOR UPDATE`, `LOCK IN SHARE MODE`를 차단합니다. |
| Qualified Source | 모든 실제 table을 `schema.table`로 작성해야 합니다. |
| Schema Allowlist | target에 등록되지 않은 schema를 연결 전에 거부합니다. |
| Bounded Limit | SQL `LIMIT`은 거부하고 최대 500인 tool `limit`만 허용합니다. |
| Driver Defense | MySQL driver의 multiple statements를 비활성화합니다. |
> [!IMPORTANT]
> SQL 검증은 보조 방어선입니다. 모든 target은 반드시 별도의 최소 권한 읽기 전용 DB 계정을 사용해야 합니다. 자세한 내용은 [Security policy](./SECURITY.md)를 확인하세요.
## SSH 연결
```text
allocated loopback host:port
└── bastion user@host
└── database host:port
```
- local host는 `127.0.0.1`, `localhost`, `::1`만 허용합니다.
- `BatchMode=yes`를 사용하므로 key와 `known_hosts`를 미리 준비해야 합니다.
- `bastionPortEnv`, `identityFileEnv`, `localHostEnv`, `localPortEnv`는 선택값입니다.
- 같은 SSH target의 요청은 직렬화하고 서로 다른 target은 독립적으로 처리합니다.
- 터널 준비 실패나 query 오류가 발생해도 SSH process와 DB connection을 정리합니다.
## 공통 제한값
| 변수 | 기본값 | 설명 |
| --- | ---: | --- |
| `DB_CONNECT_TIMEOUT_MS` | `5000` | DB 연결 제한 시간 |
| `DB_QUERY_TIMEOUT_MS` | `10000` | 쿼리 실행 제한 시간 |
| `DB_TUNNEL_WAIT_TIMEOUT_MS` | `5000` | SSH 터널 준비 제한 시간 |
| `QUERY_MAX_ROWS` | `100` | tool `limit`의 기본·최대 행 수 |
| `DB_MCP_DASHBOARD_PORT` | `7331` | 로컬 웹 대시보드 port |
- `QUERY_MAX_ROWS`의 하드 상한은 `500`입니다.
- query 안에 직접 작성한 `LIMIT`은 거부됩니다.
## 개발
```bash
npm ci
npm run typecheck
npm run build
npm test
npm run check
```
| 명령어 | 설명 |
| --- | --- |
| `npm start` | 기본 config로 stdio MCP 서버를 실행합니다. |
| `npm run dashboard` | build 후 로컬 웹 대시보드를 실행합니다. |
| `npm run validate` | 기본 config와 모든 환경 변수 참조를 검증합니다. |
| `npm test` | 설정, CLI, MCP, SQL, router와 tunnel 테스트를 실행합니다. |
| `npm run typecheck` | 소스와 테스트를 TypeScript strict 모드로 검사합니다. |
| `npm run build` | Node ESM 실행물과 선언 파일을 `dist/`에 생성합니다. |
| `npm run check` | 전체 TypeScript 타입 검사를 실행합니다. |
## 프로젝트 구조
```text
db-mcp/
├── src/
│ ├── cli.ts # init·dashboard·validate·list·test·serve CLI
│ ├── index.ts # runtime 조립과 stdio server lifecycle
│ ├── types.ts # config·registry·query 공통 타입
│ ├── dashboard-store.ts # YAML·env 안전 저장과 dashboard view model
│ ├── dashboard-server.ts # loopback HTTP API와 보안 경계
│ ├── dashboard-ui.ts # dependency-free responsive web UI
│ ├── config.ts # YAML schema와 env reference 검증
│ ├── targets.ts # dynamic target registry
│ ├── server.ts # 9개 MCP tools
│ ├── sql-policy.ts # read-only SQL과 schema 정책
│ ├── db-router.ts # direct / SSH routing
│ ├── tunnel.ts # SSH forwarding과 자동 port
│ ├── keyed-lock.ts # target 단위 요청 직렬화
│ └── load-env.ts # config 기준 .env loading
├── dist/ # NodeNext ESM build와 .d.ts (Git 제외)
├── tsconfig.json # strict 소스·테스트 타입 검사
├── tsconfig.build.json # 배포용 dist build 설정
├── scripts/start.sh # Codex용 고정 경로 launcher
├── docs/configuration.md # 전체 config reference
├── test/ # unit·contract·CLI tests
├── .env.example
└── db-mcp.config.example.yaml
```
## Contributing
- 실제 자격증명이나 조직 전용 target은 tracked config에 추가하지 마세요.
- 동작 변경에는 config, policy, routing 또는 MCP contract 테스트를 함께 추가하세요.
- 기여 절차는 [CONTRIBUTING.md](./CONTRIBUTING.md)를 따릅니다.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues