Skip to main content
Glama
m-umemura-pitdesign

MCP Parking Server

README.md
# MCP Parking Server

駐車場管理システム用の読み取り専用MCPサーバーです。
自然言語の質問に対して、データベースを参照し日本語で回答します。

## 機能

- 駐車場の検索(名前・コード・ID)
- 課金設定の取得(再課金猶予時間、夜間料金など)
- 読み取り専用SQLの実行
- スキーマコンテキストによる業務理解支援

## セットアップ

### 1. 依存関係のインストール

```bash
pip install -e .
```

### 2. 環境変数の設定

```bash
cp .env.example .env
# .env ファイルを編集してデータベース接続情報を設定
```

### 3. データベースユーザーの作成(推奨)

```sql
-- 読み取り専用ユーザーを作成
CREATE USER 'readonly_user'@'%' IDENTIFIED BY 'secure_password';

-- SELECT権限のみを付与
GRANT SELECT ON parking_system.parkings TO 'readonly_user'@'%';
GRANT SELECT ON parking_system.parking_configs TO 'readonly_user'@'%';
GRANT SELECT ON parking_system.parking_rates TO 'readonly_user'@'%';
GRANT SELECT ON parking_system.parking_sessions TO 'readonly_user'@'%';

FLUSH PRIVILEGES;
```

### 4. サーバーの起動

```bash
mcp-parking-server
```

## MCP クライアントからの接続

### Claude Desktop での設定例

`claude_desktop_config.json`:
```json
{
  "mcpServers": {
    "parking": {
      "command": "python",
      "args": ["-m", "src.server"],
      "cwd": "/path/to/mcp-parking-server",
      "env": {
        "DB_HOST": "localhost",
        "DB_PORT": "3306",
        "DB_USER": "readonly_user",
        "DB_PASSWORD": "secure_password",
        "DB_NAME": "parking_system"
      }
    }
  }
}
```

## ツール一覧

| ツール名 | 説明 |
|---------|------|
| `search_parking` | 駐車場を検索(名前・コード・ID) |
| `get_parking_config` | 駐車場の設定を取得 |
| `get_night_rate_config` | 夜間料金設定を取得 |
| `execute_readonly_sql` | 読み取り専用SQLを実行 |
| `get_schema_context` | スキーマの業務コンテキストを取得 |
| `explain_term` | 業務用語を説明 |
| `suggest_approach` | クエリアプローチを提案 |

## プロジェクト構造

```
mcp-parking-server/
├── src/
│   ├── __init__.py
│   ├── server.py          # MCPサーバー本体
│   ├── database.py        # データベース接続
│   ├── schema_context.py  # スキーマコンテキスト管理
│   ├── sql_validator.py   # SQLバリデーション
│   └── tools.py           # ツール定義
├── config/
│   └── schema_context.json # スキーマ定義
├── docs/
│   └── examples.md        # 使用例
├── .env.example
├── pyproject.toml
└── README.md
```

## セキュリティ

詳細は下記「セキュリティガードレール」セクションを参照してください。

---

# セキュリティガードレール

このMCPサーバーは、エンタープライズ環境での安全な運用を前提に設計されています。

## 1. 多層防御アーキテクチャ

```
┌─────────────────────────────────────────────────────────┐
│                    MCP Client (Claude)                  │
└─────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────┐
│  Layer 1: ツールレベル制限                               │
│  - 事前定義されたツールのみ使用可能                       │
│  - 入力パラメータのバリデーション                        │
└─────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────┐
│  Layer 2: SQLバリデーション (sql_validator.py)          │
│  - SELECT文のみ許可                                      │
│  - 禁止キーワード・関数のブロック                        │
│  - テーブルホワイトリスト                                │
│  - LIMIT強制                                             │
└─────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────┐
│  Layer 3: データベース接続 (database.py)                │
│  - 読み取り専用トランザクション                          │
│  - クエリタイムアウト                                    │
│  - 接続プール制限                                        │
└─────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────┐
│  Layer 4: データベースユーザー権限                       │
│  - SELECT権限のみ付与                                    │
│  - 対象テーブル限定                                      │
└─────────────────────────────────────────────────────────┘
```

## 2. SQLバリデーション詳細

### 2.1 禁止キーワード

以下のキーワードを含むクエリは拒否されます:

| カテゴリ | 禁止キーワード |
|---------|---------------|
| DML(データ変更) | INSERT, UPDATE, DELETE, REPLACE, TRUNCATE, MERGE |
| DDL(スキーマ変更) | CREATE, ALTER, DROP, RENAME |
| DCL(権限管理) | GRANT, REVOKE |
| トランザクション | COMMIT, ROLLBACK, SAVEPOINT |
| その他 | LOAD, HANDLER, CALL, EXECUTE, PREPARE |
| ファイル操作 | INTO OUTFILE, INTO DUMPFILE, LOAD_FILE |

### 2.2 禁止関数

SQLインジェクション対策として、以下の関数を禁止:

- `SLEEP()` - 時間ベース攻撃
- `BENCHMARK()` - 時間ベース攻撃
- `LOAD_FILE()` - ファイル読み取り

### 2.3 テーブルホワイトリスト

アクセス可能なテーブルを明示的に制限:

```python
ALLOWED_TABLES = [
    "parkings",
    "parking_configs",
    "parking_rates",
    "parking_sessions",
]
```

### 2.4 その他の制限

| 制限項目 | 内容 |
|---------|------|
| 複文実行 | セミコロンによる複数SQL実行を禁止 |
| コメント | `--`, `/**/`, `#` を禁止 |
| UNION | UNIONインジェクション対策として禁止 |
| サブクエリ | 最大1階層まで |
| LIMIT | 必須(自動付与)、最大1000件 |

## 3. データベースレベルの保護

### 3.1 読み取り専用トランザクション

```python
# 接続時に読み取り専用モードを設定
await cursor.execute("SET SESSION TRANSACTION READ ONLY")
```

### 3.2 タイムアウト制御

```python
# クエリタイムアウト(デフォルト30秒)
await asyncio.wait_for(
    cursor.execute(query, params),
    timeout=self.config.query_timeout
)
```

### 3.3 接続プール制限

```python
# 同時接続数を制限
pool_min_size=1
pool_max_size=5
```

## 4. 推奨されるデータベース設定

### 4.1 専用の読み取り専用ユーザー

```sql
-- 最小権限の原則に従ったユーザー作成
CREATE USER 'mcp_readonly'@'%' IDENTIFIED BY 'strong_password';

-- 必要なテーブルにのみSELECT権限を付与
GRANT SELECT ON parking_system.parkings TO 'mcp_readonly'@'%';
GRANT SELECT ON parking_system.parking_configs TO 'mcp_readonly'@'%';
GRANT SELECT ON parking_system.parking_rates TO 'mcp_readonly'@'%';
GRANT SELECT ON parking_system.parking_sessions TO 'mcp_readonly'@'%';

-- 権限を反映
FLUSH PRIVILEGES;
```

### 4.2 リードレプリカの使用(推奨)

本番環境では、リードレプリカに接続することを推奨します:

```
DB_HOST=replica.parking-db.internal
```

## 5. 監査とログ

### 5.1 アプリケーションログ

```python
# 全てのツール呼び出しをログ記録
logger.info(f"Tool called: {name} with args: {arguments}")
```

### 5.2 推奨される追加対策

- クエリログの永続化(CloudWatch Logs等)
- 異常なクエリパターンのアラート設定
- 定期的なアクセスログレビュー

## 6. プライバシー保護

### 6.1 個人情報の取り扱い

`parking_sessions.vehicle_number`(車両番号)は個人情報に該当する可能性があります。
スキーマコンテキストでこれを明示し、不必要なアクセスを抑制します:

```json
{
  "privacy_note": "vehicle_number は個人情報に該当する可能性があるため、必要最小限の参照に留める"
}
```

### 6.2 データマスキング(オプション)

必要に応じて、機密カラムのマスキングを実装できます:

```sql
-- マスキングの例
SELECT
    id,
    CONCAT(LEFT(vehicle_number, 2), '****') as vehicle_number_masked
FROM parking_sessions
```

## 7. 障害対策

| 障害シナリオ | 対策 |
|-------------|------|
| DB接続失敗 | 明確なエラーメッセージを返却 |
| クエリタイムアウト | 30秒でタイムアウト、エラー返却 |
| 不正なSQL | バリデーションエラーを返却 |
| 大量データ | LIMITによる件数制限 |

## 8. セキュリティチェックリスト

デプロイ前に以下を確認してください:

- [ ] データベースユーザーはSELECT権限のみ持っている
- [ ] 環境変数でパスワードを管理している(ハードコードしていない)
- [ ] リードレプリカまたは専用の参照用DBに接続している
- [ ] ネットワークアクセスが制限されている(VPC内など)
- [ ] アプリケーションログが有効になっている
- [ ] schema_context.json のテーブル一覧が実際のホワイトリストと一致している