loading…
Search for a command to run...
loading…
A read-only MCP server for querying parking management systems using natural language and secure SQL. It enables searching for parking locations, retrieving bil
A read-only MCP server for querying parking management systems using natural language and secure SQL. It enables searching for parking locations, retrieving billing configurations, and understanding database schemas through business context tools.
駐車場管理システム用の読み取り専用MCPサーバーです。 自然言語の質問に対して、データベースを参照し日本語で回答します。
pip install -e .
cp .env.example .env
# .env ファイルを編集してデータベース接続情報を設定
-- 読み取り専用ユーザーを作成
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;
mcp-parking-server
claude_desktop_config.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サーバーは、エンタープライズ環境での安全な運用を前提に設計されています。
┌─────────────────────────────────────────────────────────┐
│ MCP Client (Claude) │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Layer 1: ツールレベル制限 │
│ - 事前定義されたツールのみ使用可能 │
│ - 入力パラメータのバリデーション │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Layer 2: SQLバリデーション (sql_validator.py) │
│ - SELECT文のみ許可 │
│ - 禁止キーワード・関数のブロック │
│ - テーブルホワイトリスト │
│ - LIMIT強制 │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Layer 3: データベース接続 (database.py) │
│ - 読み取り専用トランザクション │
│ - クエリタイムアウト │
│ - 接続プール制限 │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Layer 4: データベースユーザー権限 │
│ - SELECT権限のみ付与 │
│ - 対象テーブル限定 │
└─────────────────────────────────────────────────────────┘
以下のキーワードを含むクエリは拒否されます:
| カテゴリ | 禁止キーワード |
|---|---|
| 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 |
SQLインジェクション対策として、以下の関数を禁止:
SLEEP() - 時間ベース攻撃BENCHMARK() - 時間ベース攻撃LOAD_FILE() - ファイル読み取りアクセス可能なテーブルを明示的に制限:
ALLOWED_TABLES = [
"parkings",
"parking_configs",
"parking_rates",
"parking_sessions",
]
| 制限項目 | 内容 |
|---|---|
| 複文実行 | セミコロンによる複数SQL実行を禁止 |
| コメント | --, /**/, # を禁止 |
| UNION | UNIONインジェクション対策として禁止 |
| サブクエリ | 最大1階層まで |
| LIMIT | 必須(自動付与)、最大1000件 |
# 接続時に読み取り専用モードを設定
await cursor.execute("SET SESSION TRANSACTION READ ONLY")
# クエリタイムアウト(デフォルト30秒)
await asyncio.wait_for(
cursor.execute(query, params),
timeout=self.config.query_timeout
)
# 同時接続数を制限
pool_min_size=1
pool_max_size=5
-- 最小権限の原則に従ったユーザー作成
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;
本番環境では、リードレプリカに接続することを推奨します:
DB_HOST=replica.parking-db.internal
# 全てのツール呼び出しをログ記録
logger.info(f"Tool called: {name} with args: {arguments}")
parking_sessions.vehicle_number(車両番号)は個人情報に該当する可能性があります。
スキーマコンテキストでこれを明示し、不必要なアクセスを抑制します:
{
"privacy_note": "vehicle_number は個人情報に該当する可能性があるため、必要最小限の参照に留める"
}
必要に応じて、機密カラムのマスキングを実装できます:
-- マスキングの例
SELECT
id,
CONCAT(LEFT(vehicle_number, 2), '****') as vehicle_number_masked
FROM parking_sessions
| 障害シナリオ | 対策 |
|---|---|
| DB接続失敗 | 明確なエラーメッセージを返却 |
| クエリタイムアウト | 30秒でタイムアウト、エラー返却 |
| 不正なSQL | バリデーションエラーを返却 |
| 大量データ | LIMITによる件数制限 |
デプロイ前に以下を確認してください:
Добавь это в claude_desktop_config.json и перезапусти Claude Desktop.
{
"mcpServers": {
"mcp-parking-server": {
"command": "npx",
"args": []
}
}
}