# 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 のテーブル一覧が実際のホワイトリストと一致している