mcpField
M
MySQL (security-first)

MySQL (security-first)

ローカル
github.comデータベース26 週間前に更新

MySQL (security-first) について

MySQL (security-first)は、SQL AST解析、デフォルト拒否のテーブル許可リスト、実行制限でアクセスを管理するMySQL MCPサーバーです。スキーマリソース、クエリプラン分析、クエリ統計も提供します。

主な機能

  1. 01SQL AST解析によるステートメント検証
  2. 02デフォルト拒否のテーブル許可リスト
  3. 03ステートメント種別ごとのアクセス設定
  4. 04行数、時間、複数ステートメント実行の制限
  5. 05スキーマリソースとクエリプランの提供
  6. 06クエリ統計と任意の監査記録

活用シーン

  1. 01定義済みの制限下でエージェントにMySQLを照会させる
  2. 02複数のデータベース接続設定を分離する
  3. 03遅いクエリと実行プランを調べる
  4. 04リソースとしてクライアントにスキーマ情報を提供する

MCP 対応

MCPサーバーは、MySQLのクエリ、制限付き書き込み、スクリプト、テーブル一覧、統計のツールをクライアントに提供し、許可リストにあるベーステーブルのスキーマリソースを公開します。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。

mcp.json · 共通設定
{
"mcpServers": {
"kurok1-mcp-server-mysql": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"MYSQL_MCP_PASSWORD",
"ghcr.io/kurok1/mcp-server-mysql:2.1.0"
],
"env": {
"MYSQL_MCP_PASSWORD": "<MYSQL_MCP_PASSWORD>"
}
}
}
}
起動コマンドdocker run -i --rm -e MYSQL_MCP_PASSWORD ghcr.io/kurok1/mcp-server-mysql:2.1.0
設定が必要な環境変数
MYSQL_MCP_PASSWORDシークレットMySQL password, referenced from config.yaml as ${MYSQL_MCP_PASSWORD}

ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。

パッケージ
ocighcr.io/kurok1/mcp-server-mysql:2.1.0

ツール

ツール一覧はまだ取得されていません

ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。

詳細

mcp-server-mysql

英語 | 简体中文

Release License Go Docker

セキュリティを最優先に設計した MySQL MCP サーバーです。 データベースにアクセスする SQL 文はすべて、産業グレードの SQL パーサー(TiDB parser)による完全な AST 解析を通過する必要があります。さらに、読み取り専用トランザクションへのフォールバックと、ドライバーレベルでの複数ステートメントの禁止によって保護します。独立した 3 つの多層防御により、AI がデータベースにクエリを実行できても、データベースを持ち去ることはできません。

このサーバーを選ぶ理由

ほとんどの MySQL MCP サーバーは、正規表現やキーワードの照合、またはクエリを読み取り専用トランザクションで囲む方法で「読み取り専用」を実現しています。しかし、どちらの方法にも問題があります。

  • 正規表現によるチェックは、SQL コメントや巧妙な書き換えによって回避できます。
  • 読み取り専用トランザクションだけでは、COMMIT; DROP TABLE ... を連結するステートメントインジェクションを防げません。これは Datadog が公式 Postgres リファレンスサーバーに対して実証した攻撃と同じもので、そのサーバーは後にアーカイブされています。

このプロジェクトでは、セキュリティ境界を実際の SQL セマンティクス解析に設けています。すべてのステートメントは TiDB parser(MySQL 8.0 の文法に対応)によって AST に解析され、パーサーが解釈できないものは拒否されます。つまり、フェイルクローズの設計により、文法の対応範囲が不完全でも過剰にブロックすることはあっても、ブロック漏れは発生しません。また、パーサーは実際の MySQL セマンティクスを認識するため、バージョン付きコメント /*!80000 ... */ の中に JOIN mysql.user を隠すような手法も、ほかのテーブル参照と同様に展開してチェックします。

主な特長

  • 独立した接続プロファイル — 1つのプロセスで複数のMySQL接続を管理でき、それぞれが独自の接続プール、セキュリティルール、監査ファイル、統計情報を持ちます。list_profileで名前と説明を確認し、すべてのSQLツールに明示的なprofileを渡します。
  • ステートメント種別ごとの制御 — SELECT/INSERT/UPDATE/DELETE/DDLは個別に切り替えられます。デフォルトは読み取り専用です。SET、GRANT、CALL、USE、LOAD DATA、LOCK TABLES、およびトランザクション制御(BEGIN/COMMIT/ROLLBACK)は常に拒否されます。分類自体が許可リスト方式のため、未知のステートメント種別は仕組み上、拒否側に分類されます。
  • デフォルト拒否のテーブル許可リスト — 許可リストに追加するまで、テーブルは一切表示されません。db.*、db.table、app_*.logsなどのパターンを使用できます(各側でグロブを使用し、大文字と小文字は区別しません)。すべてのテーブル参照をASTから抽出します。JOIN、サブクエリ、派生テーブル、CTE(スコープを認識するため、CTE名が実テーブル名を隠してチェックをすり抜けることはありません)、複数テーブルを対象とするDML、INSERT ... SELECT、バージョンコメントが対象です。
  • MCPテーブルスキーマリソース — MCP接続の初期化後、権限上参照可能で許可リストに登録されたすべてのベーステーブルが、mysql:///schema/{profile}/{database}/{table}として公開されます。リソースを読み込むと、ライブのSHOW CREATE TABLE SQLが返されます。テーブルレベルで変動するAUTO_INCREMENT=Nカウンターのみが削除されます。
  • 実行時のガードレール — 行数の上限、クエリごとのタイムアウト、単一ステートメントの強制、WHEREなしのUPDATE/DELETEを検出するトリップワイヤーを備えています。
  • 組み込みの可観測性 — クエリごとのレイテンシーと行数、低速クエリのフラグ付け、および「最も遅かったクエリは?」と会話内で尋ねられるmysql_statsツールを備えています。
  • オプトインの構造化監査 — 日次ローテーションに対応したJSONL形式です。拒否されたSQLは、該当したルールを正確に記録します。デフォルトでは無効のため、有効化しない限りログファイルは作成されません。
  • アトミックなスクリプト — mysql_scriptは複数ステートメントのスクリプトを単一のトランザクション内で実行し、各ステートメントを個別に再検証します。失敗があればすべてロールバックされます。MySQLの暗黙的なコミットによってアトミック性が損なわれるため、スクリプト内のDDLは禁止されています。
  • クエリプラン分析 — traditional/json/tree形式とEXPLAIN ANALYZEに対応したmysql_explainを利用できます。
  • 対話型クエリ結果 — MCP Apps対応ホストでは、mysql_queryの結果が、ビューごとの履歴、選択、列の操作、TSV/CSV/JSON形式でのコピーが可能な、フィルターと並べ替えに対応したテーブルとして表示されます。それ以外のホストには、従来どおりテキスト形式の結果が返されます。
  • 実行が簡単で、信頼しやすい設計 — デフォルトでstdioを使用する単一の静的Goバイナリです。公式のMCP Go SDKを基盤とし、Dockerイメージはdistrolessで、非rootユーザーとして実行されます。

ツール

7 つの mysql_* ツールはすべて、設定済みの名前と一致する空でない profile 文字列を必要とします。デフォルトの接続先はありません。指定されていないプロファイルや未知のプロファイルは、データベースにアクセスする前に拒否されます。mysql_describe_table の省略可能な database 引数のデフォルト値は、選択したプロファイルのデータベースです。

ツール機能
list_profile引数は不要です。名前と説明を名前順にテキストおよび構造化形式 {profiles: [{name, description}]} で返します。接続の確認や認証情報の取得は行いません
mysql_query読み取り専用のステートメント(SELECT / SHOW / DESCRIBE / EXPLAIN)を 1 つ実行します
mysql_execute書き込みステートメント(INSERT / UPDATE / DELETE / DDL。各タイプは設定で有効化する必要があります)を 1 つ実行し、影響を受けた行数を返します
mysql_script; 区切りの複数ステートメントのスクリプトを 1 つのトランザクション内でアトミックに実行します。すべて成功するか、すべてロールバックされます。DDL は禁止されています
mysql_explain単一の SELECT の実行計画を返します(format:traditional / json / tree。analyze: true を指定すると EXPLAIN ANALYZE を実行します)
mysql_list_tablesホワイトリストを通じて参照可能なベーステーブルを一覧表示します
mysql_describe_tableホワイトリストに登録されたテーブルのカラム構造を返します
mysql_stats選択したプロファイルのプロセス稼働中の統計ウィンドウです。プロファイル名、合計数/拒否数、平均および P95 レイテンシ、実行時間の長いクエリの上位 N 件、テーブルごとのアクセス数を返します

たとえば、まず {} を指定して list_profile を呼び出し、次に {"profile":"dev","sql":"SELECT * FROM myapp.orders LIMIT 10"} を指定して mysql_query を呼び出すか、{"profile":"dev","top_n":5} を指定して mysql_stats を呼び出します。データの確認、変更、検証では、同じプロファイルを使用してください。

リソース

サーバーは MCP の初期化/検出時に各プロファイルのテーブルスナップショットを取得し、参照可能なベーステーブルごとに直接リソースを登録します。リソース名にはプロファイルも含まれるため、異なるサーバー上に同じデータベース名とテーブル名があっても区別できます。

URIMIME タイプ内容
mysql:///schema/{profile}/{database}/{table}application/sql現在の正規化された SHOW CREATE TABLE の出力

「参照可能」とは、そのプロファイルの MySQL アカウントが参照できるテーブルと、security.table_whitelist の両方に含まれるテーブルを指します。ビューは登録されません。リソースの検出には security.max_rows による上限が適用されません。また、allowed_statements に select が含まれていない場合でも、リソースの読み取りは引き続き可能です。どちらの操作も、ユーザーが送信した SQL ではなく、サーバー側で固定されたメタデータ SQL を実行するためです。

リソースのセットは検出時のスナップショットです。後から作成されたテーブルは、次回の検出または再接続後に表示されます。削除されたテーブルや、新たにアクセスできなくなったテーブルを参照すると、MCP Resource Not Found が返されます。リソースの内容は最新の状態です。そのため、ALTER TABLE の結果は次回の読み取りに反映されます。リソースの検出と読み取りは監査ログや mysql_stats に記録されません。検出に失敗した場合はプロファイル名とともにログに記録され、そのプロファイルのテーブルリソースのみがクリアされます。他のプロファイル、ツール、共有クエリ結果 App は引き続き利用できます。

ルートレベルの resources.enabled スイッチは、すべてのプロファイルに適用されます。リソース機能全体を無効にするには、false に設定します。デフォルトは true です。無効にすると、サーバーは Resources を通知せず、テーブルリソースまたは MCP App リソースを登録せず、初期化/検出時にどのプロファイルの MySQL に対してもクエリを実行しません。そのため、インタラクティブな MCP App は利用できなくなりますが、mysql_query のテキスト形式および構造化形式の結果を含む 8 つすべてのツールは引き続き利用できます。

クイックスタート

1. バイナリを入手する

ビルド済み — Releases からお使いのプラットフォーム用の tarball(linux_amd64 / linux_arm64 / darwin_arm64)をダウンロードしてください(チェックサムを含みます)。または、Go を使ってインストールしてください。

go install github.com/Kurok1/mcp-server-mysql/cmd/mcp-server-mysql@latest

Docker — マルチアーキテクチャイメージは GitHub Container Registry に公開されています。

docker pull ghcr.io/kurok1/mcp-server-mysql:latest
2. 設定する

config.example.yaml をコピーして、次のように調整します。

mkdir -p ~/.mcp-server-mysql
cp config.example.yaml ~/.mcp-server-mysql/config.yaml

最小構成の設定例:

resources:
  enabled: true
profiles:
  dev:
    description: Development database
    mysql:
      host: 127.0.0.1
      port: 3306
      user: mcp_dev                  # use a dedicated least-privilege account, not root
      password: ${MYSQL_MCP_PASSWORD}
      database: myapp

    security:
      allowed_statements: [select]   # add insert/update/delete/ddl only if you need them
      table_whitelist:
        - "myapp.*"

他の接続については、profiles の下に同階層のエントリーを追加します。各エントリーでは、同じ mysql、security、audit フィールドを使用できます。注釈付きのサンプルには、2つのプロファイルが含まれています。以下のクライアントの例では、この最小構成を使用します。他の構成の場合は、参照されているすべてのパスワード環境変数を MCP プロセスに渡してください。

3. MCP クライアントに接続する

Claude Code:

claude mcp add mysql --env MYSQL_MCP_PASSWORD=your-password -- \
  ~/go/bin/mcp-server-mysql --config ~/.mcp-server-mysql/config.yaml

Claude Desktop または JSON で設定するクライアント:

{
  "mcpServers": {
    "mysql": {
      "command": "/usr/local/bin/mcp-server-mysql",
      "args": ["--config", "/Users/me/.mcp-server-mysql/config.yaml"],
      "env": { "MYSQL_MCP_PASSWORD": "..." }
    }
  }
}

Docker:

{
  "mcpServers": {
    "mysql": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-v", "/Users/me/.mcp-server-mysql:/data",
        "-e", "MYSQL_MCP_PASSWORD",
        "ghcr.io/kurok1/mcp-server-mysql:latest",
        "--config", "/data/config.yaml"
      ],
      "env": { "MYSQL_MCP_PASSWORD": "..." }
    }
  }
}

Docker に関する注意 1 — 監査ログはマウントしたボリュームに保存する必要があります。 コンテナはセッション終了時に破棄されます。監査ログを有効にする場合は、audit.log_dir をマウントしたボリューム(例:/data/logs)に設定してください。そうしないと、ログはコンテナとともに消失します。

Docker に関する注意 2 — ホスト上の MySQL への接続。 macOS/Windows では mysql.host: host.docker.internal を設定します。Linux では、args に "--add-host=host.docker.internal:host-gateway" も追加してください。

インタラクティブなクエリ結果(MCP Apps)

mysql_query は、MCP Apps に対応するホストに、埋め込みリソース ui://mcp-server-mysql/query-results を通知します。成功した呼び出しには、既存の人が読めるテキストと、必須の profile フィールドを含む構造化クエリデータの両方が含まれるため、古いホストやテキストのみを扱うホストでも、結果情報を失わずに利用できます。クエリエラーでは、テキストを維持し、結果メタデータに元の {profile, sql} が含まれるため、App はリクエストを識別できます。list_profile もテキストと構造化データを返し、残りの6つのツールはテキストの結果を使用します。

結果ビューでは、現在の View 内に最大20件のスナップショットを保持し、全体および任意の status による絞り込み、自然な数値順の並べ替え、列の表示切り替え、行選択、TSV/CSV/JSON 形式でのコピーに対応しています。結果と履歴には、失敗したリクエストやキャンセルされたリクエストも含め、プロファイルが表示されます。更新すると、選択したスナップショットの元の {profile, sql} を使用して、ホスト経由で mysql_query が呼び出されます。履歴を切り替えた後も同様です。ホストがアプリにツール呼び出し機能を公開していない場合、ビューには更新が利用できないことが表示されますが、読み取り専用の操作は引き続き利用できます。ビューの履歴はメモリ内にのみ保存され、View を閉じると消去されます。

重複するリクエストで、リクエストを特定するのに十分な情報がないホストエラーまたはキャンセル通知を受け取った場合、履歴ではその発生元を未確定として示し、候補となる入力を保持します。通知の到着順に基づいて、その通知をプロファイルに割り当てることはありません。

ローカルでの Basic Host 開発

本番環境およびデフォルトのトランスポートは引き続き stdio です。ローカルでの MCP Apps 開発専用に、ステートレスな Streamable HTTP エンドポイントを利用できます。

MYSQL_MCP_PASSWORD=your-password mcp-server-mysql \
  --config ~/.mcp-server-mysql/config.yaml \
  --transport streamable-http \
  --listen 127.0.0.1:3001

公式の Basic Host を http://127.0.0.1:3001/mcp に接続します。HTTP リスナーはワイルドカードアドレスおよびループバック以外のアドレスを拒否し、CORS ポリシーではローカルポート 8080 の Basic Host オリジンのみを許可します。これは、認証されたリモートデプロイモードではありません。

セキュリティモデル

            MCP client  (Claude Code / Claude Desktop / …)
                               │  stdio
                               ▼
┌───────────────────────  mcp-server-mysql  ───────────────────────┐
│                                                                  │
│  Table resources: mysql:///schema/{profile}/{database}/{table}  │
│  fixed metadata SQL · base tables only · whitelist-filtered      │
│                                                                  │
│  mysql_query · mysql_execute · mysql_script · mysql_explain      │
│  mysql_list_tables · mysql_describe_table · mysql_stats          │
│  required profile → Manager.Get → independent runtime           │
│  list_profile → configured names/descriptions (no database IO)  │
│                             │                                    │
│                             ▼                                    │
│  ┌ Layer 1 · AST main gate (TiDB parser) ───────────────────┐    │
│  │ unparseable ⇒ denied (fail-closed) → single statement    │    │
│  │ → statement-class allowlist + read/write tool check      │    │
│  │ → per-class switches (default: select only)              │    │
│  │ → dangerous constructs (INTO OUTFILE / LOAD_FILE)        │    │
│  │ → missing-WHERE tripwire                                 │    │
│  │ → default-deny table whitelist (JOIN / subquery /        │    │
│  │   CTE scope-aware / versioned comments)                  │    │
│  └────────────┬───────────────────────────────┬─────────────┘    │
│               │ allowed                       │ denied           │
│               ▼                               ▼                  │
│  ┌ Layer 2 · executor ─────────────┐    DENIED [rule]: reason    │
│  │ single-stmt reads: READ ONLY tx │    is returned to the model │
│  │ row cap · query timeout         │    with the rule name       │
│  └────────────┬────────────────────┘                             │
│               ▼                                                  │
│  ┌ Layer 3 · driver ───────────────┐                             │
│  │ multiStatements=false: stacked  │                             │
│  │ injection impossible            │                             │
│  └────────────┬────────────────────┘                             │
│               │   guard decisions — allowed & denied — go to     │
│               │   audit: ring buffer (+ optional JSONL files)    │
└───────────────┬──────────────────────────────────────────────────┘
                ▼
      MySQL  —  Layer 0: dedicated least-privilege account

レイヤー 0 — MySQL アカウント(強く推奨)。 使用する権限だけを持つ専用アカウントでサーバーを実行してください(読み取り専用のワークロードでは SELECT のみを付与します)。root は使用しないでください。これは、以下のすべての対策が強化する封じ込めレイヤーです。

レイヤー 1 — AST のメインゲート。 すべてのステートメントを TiDB パーサーで解析します(解析に失敗した場合は拒否)。その後、次の順にすべてのチェックを通過する必要があります。単一ステートメントの強制 → ステートメントクラスの許可リスト(読み取り/書き込みツールとの照合も行います。書き込みが有効でも、mysql_query 経由の書き込みは拒否されます)→ クラスごとの有効化スイッチ → 危険な構文のスキャン(SELECT ... INTO OUTFILE/DUMPFILE、ネストの深さを問わない LOAD_FILE())→ WHERE の欠落を検知するトリップワイヤー → テーブル参照をすべて抽出し、デフォルト拒否のホワイトリストと照合します。

レイヤー 2 — 読み取り専用トランザクションによるフォールバック。 単一ステートメントの読み取りパス(mysql_query、mysql_explain、mysql_list_tables、mysql_describe_table)および固定リソースメタデータパスで実行される読み取りは、START TRANSACTION READ ONLY 内で実行されます。パーサーが書き込みを誤って読み取りと分類した場合でも、MySQL 自身が拒否します。(明示的に有効化した書き込みステートメントと、mysql_script 内のすべての処理(読み取りを含む)は、このバックストップの対象外です。これらでは、レイヤー 1 とレイヤー 0 が制御手段になります。)

レイヤー 3 — ドライバーレベルのロックアウト。 接続では multiStatements=false を設定するため、上記のすべてのレイヤーが機能しなかったとしても、プロトコルレベルで COMMIT; DROP TABLE ... のような連結ステートメントによるインジェクションは不可能です。

拒否の内容はすべて、機械可読なテキスト DENIED [rule_name]: reason として返されます。ルール名は固定です。

ルール発生条件
parse_errorSQL の解析に失敗した場合(フェイルクローズ。構文エラーとパーサーの未対応箇所の両方)
multi_statement1 回の呼び出しに複数のステートメントが含まれる場合
unsupported_statementSET/GRANT/CALL/USE/LOAD DATA/LOCK TABLES/トランザクション制御
wrong_toolmysql_query 経由の書き込みステートメント、または mysql_execute 経由の読み取りステートメント
statement_not_enabledステートメントクラスが allowed_statements に含まれていない場合
table_whitelist参照されるテーブルがホワイトリストの範囲外にある場合
dangerous_constructINTO OUTFILE/INTO DUMPFILE/LOAD_FILE()
unfiltered_writeWHERE 句のない UPDATE/DELETE
script_ddl/script_too_long/script_emptyスクリプト内の DDL/ステートメント数の上限を超えたスクリプト/空のスクリプト
invalid_query/not_select/invalid_format/invalid_identifiermysql_explain/mysql_describe_table のパラメーター検証

mysql_script の拒否では、理由の先頭に問題のあるステートメントの位置が付加されます。DENIED [rule]: statement N: reason

ガードはテストスイートの中核です。約 100 件のテーブル駆動型テストで、連結ステートメントによるインジェクション、バージョン付きコメントを使ったすり抜け、CTE のシャドーイングによるホワイトリストの回避、INSERT ... SELECT のテーブル抽出などを網羅しています。ホワイトリストの適用、READ ONLY のバックストップによる書き込み拒否、スクリプトのロールバック、EXPLAIN ツリーによる拒否などのエンドツーエンドテストは、testcontainers 内の実際の MySQL 8.0 に対して実行されます。

細かな注意点

検証できないセキュリティドキュメントは、単なる宣伝です。正確な境界は次のとおりです。

  • 読み取り専用トランザクションによる保護が適用されるのは、単一ステートメントの読み取りパスです。明示的に有効化した書き込みタイプと、mysql_script 内のすべてのステートメント(読み取りも含みます。スクリプト内では読み取りと書き込みが同じトランザクションを共有するため)は、この保護なしで実行されます。その場合の制御は、AST ゲートとデータベースアカウントの権限(レイヤー 0)です。
  • unfiltered_write は**WHERE 句の欠落を検知するトリップワイヤー**であり、テーブル全体への書き込みを防ぐものではありません。UPDATE t SET a=1 WHERE 1=1 は通過します。これはミスを検知するものであり、悪意ある操作を防ぐものではありません。
  • mysql_list_tables と MCP リソースの検出・読み取りには、固定されたユーザー指定不可のメタデータ SQL が使われます。テーブルの検出では information_schema に対してベーステーブルを問い合わせ、すべての結果をホワイトリストでフィルタリングします。リソースの読み取りでは、SHOW CREATE TABLE の前にホワイトリストを再確認します。EXPLAIN FORMAT=TREE も固定プレフィックスのパスです。内側の SELECT は、先にガードパイプライン全体を通過します(TiDB パーサーは FORMAT=TREE を含むステートメント全体を解析できないためです)。
  • 監査記録の対象はガードパイプラインに到達した SQL で、許可されたものと拒否されたものの両方です。監査対象外は、MCP リソースの検出・読み取り、mysql_stats の呼び出し、mysql_describe_table の事前チェックによる拒否(invalid_identifier およびテーブル名の table_whitelist チェック)、mysql_explain のパラメーターによる拒否(invalid_query、not_select、invalid_format)です。スクリプトの監査は実際の実行内容に従います。ガードによってスクリプトが拒否された場合は、スクリプト全体で 1 件の記録が生成されます。また、失敗したステートメントの後に続くステートメントは検証されても実行されないため、記録されません。
  • MySQL 接続は通常の TCPのみです。TLS オプションも Unix ソケットもまだありません。サーバーとデータベースは信頼できるネットワーク上に置くか、接続をトンネルしてください。

設定

profiles は空ではないマップです。名前は [a-z0-9][a-z0-9_-]* に一致する必要があります。description は省略可能で、省略した場合は空文字列になります。各プロファイルには他のプロファイルを継承せず、同じデフォルト値が個別に適用されます。以下の表で、mysql.*、security.*、audit.*、および description は profiles.<name> の下にあり、グローバル設定は resources.enabled のみです。

注釈付きの完全な例:config.example.yaml。基本方針はデフォルトでセキュアです。allowed_statements を省略すると読み取り専用になり、table_whitelist を省略するとすべて拒否されます。block_unfiltered_writes を設定しない場合は有効になります。

また、起動時にはフェイルクローズします。ファイルを読み取れない場合、不明なキーやスペルミスのあるキーがある場合、期間が無効な場合、ホワイトリストのパターンが不正な場合、不明なステートメントタイプがある場合、スクリプトの上限値が負の場合、または mysql.user/mysql.database が未指定の場合は、すべてプロセスを中止します。異常な状態で実行を続けるのではなく、起動を拒否します。

キーデフォルト備考
description""接続の目的を人が読める形式で記述します。list_profile が返します
mysql.host127.0.0.1Docker 内からは host.docker.internal を使用します
mysql.port3306
mysql.user— 必須専用の最小権限アカウントを推奨します
mysql.password""${MYSQL_MCP_PASSWORD} を使用してください。詳細は以下を参照してください
mysql.database— 必須修飾されていないテーブル名の修飾にも使用されます
mysql.pool.max_open / max_idle5 / 2接続プール
security.allowed_statements[select]select/insert/update/delete/ddl のいずれか。SHOW/DESCRIBE/EXPLAIN は select に含まれます
security.table_whitelist[] = すべて拒否db.table パターン。左右それぞれにグロブを使用できます(myapp.*、app_*.logs)。大文字と小文字は区別されません
security.max_rows1000これを超える結果セットは、マーカーを付けて切り詰められます
security.query_timeout30sクエリごとのコンテキストタイムアウト
security.block_unfiltered_writestrueWHERE 句のない UPDATE/DELETE を拒否します
security.max_script_statements50mysql_script 呼び出しごとのステートメント上限
resources.enabledtruefalse に設定するとテーブル/UI リソースを省略し、初期化/検出時のメタデータクエリをスキップします
audit.enabledfalseJSONL ディスクログ。インメモリのセッション統計は設定にかかわらず利用できます
audit.log_dir~/.mcp-server-mysql/logsDocker ではマウントされたボリュームである必要があります
audit.slow_query_threshold1sこの時間を超えるクエリには低速フラグが付けられます
audit.ring_buffer_size1000mysql_stats の基盤となるインメモリウィンドウ

シークレットをファイルに保存する必要はありません。解析前に設定全体に環境変数展開が適用されるため、${ENV_VAR} はすべてのフィールドで使用できます。設定ファイルのパスは --config の代わりに MYSQL_MCP_CONFIG 環境変数で指定することもできます。

単一接続設定からの移行
  1. 以前のルート直下の mysql、security、audit セクションをそのまま profiles.dev のような名前の下に移動します。resources はルートに残します。
  2. 7 つすべての mysql_* ツール呼び出しに profile を追加します。利用可能な名前を確認するには list_profile を使用します。接続が 1 つだけの場合でも、暗黙の default プロファイルはありません。
  3. サーバーを再起動し、クライアントを再接続して、ツールスキーマと新しいプロファイル付き Resource URI を更新します。

レガシー形式のルート接続設定、空または無効なプロファイル、不明な設定フィールドは拒否されます。プロファイルは再起動するまで固定されます。プールの構築は遅延されます。データベースに接続できない場合、そのプロファイルのツール/リソースへのアクセス時に失敗しますが、list_profile では設定済みのエントリーが引き続き一覧表示されます。SQL ステートメントまたはスクリプトは 1 つのプロファイルを使用します。その接続内でホワイトリストにより許可された既存のスキーマ間アクセスは引き続き利用できます。プロファイルをまたぐトランザクションと実行中のプロファイル管理には対応していません。

監査ログ

ディスクへのログ記録は各プロファイルの audit.enabled で個別に制御されます。デフォルトは false です。ログファイルは作成されず、ログディレクトリも作成されません。 各プロファイルは独自のインメモリ統計リングと低速クエリのしきい値を持ちます。mysql_stats は要求されたプロファイルのウィンドウのみを読み取り、そのプロファイル名を含めます。統計はプロセス内でそのプロファイルを使用する呼び出し元の間で共有され、ディスクへのログ記録の有無にかかわらず機能し、再起動時にリセットされます。

有効にすると、JSONL ファイルは毎日ローテーションされ、audit-<profile>-<YYYY-MM-DD>.jsonl という名前になります(例:audit-dev-2026-09-15.jsonl)。プロファイル間で audit.log_dir を共有しても、ファイルは共有されません。各行は 1 つの JSON オブジェクトです。

フィールド意味
profile所有する接続プロファイル。プロファイルのロガーが設定します
ts / tool / sqlタイムスタンプ、ツール名、元の SQL
decision / ruleallowed または denied と、拒否時に適用されたルール
class / tablesステートメントの分類、参照されたテーブル
duration_ms / rowsレイテンシ、返された行数または影響を受けた行数
slow / truncated / error低速クエリのフラグ、切り詰めフラグ、エラーメッセージ

Claude Code スキル

skills/mysql-mcp は、Claude がこれらのツールを適切に使えるようにする補助スキルです。適切なツールを選び、セキュリティ境界(単一ステートメント、許可リスト、WHERE トリップワイヤー)を守り、むやみに再試行するのではなく DENIED [rule] メッセージを正しく読み取る方法を説明します。インストール方法:

cp -r skills/mysql-mcp ~/.claude/skills/

互換性

  • MySQL 8.x — E2E スイートは testcontainers 経由で MySQL 8.0(8.0.45)に対して実行されます。MySQL 5.7 と MariaDB は未テストです。
  • MCP — Go SDK v1.7.0。MySQL のテーブルスキーマを直接提供するリソース、io.modelcontextprotocol/ui 拡張、埋め込み MCP App リソースを 1 つ備えています。MCP Apps に対応していないホストでは、テキストフォールバックを引き続き利用できます。
  • トランスポート — デフォルトは stdio です。ローカル開発では、ループバック限定のステートレスな Streamable HTTP を利用できます。サーバーの識別子は mcp-server-mysql です。8 つのツール、テーブルスキーマを直接提供するリソース、共有 UI リソースを 1 つ公開し、プロンプトは公開しません。

開発

go test ./... -short           # unit tests (no Docker needed)
go test -race ./... -short     # concurrency checks, including profile isolation
go test ./... -timeout 600s    # full suite incl. testcontainers integration/E2E (needs Docker)

cd ui/query-results
npm ci
npm run typecheck
npm test
npm run build                  # rebuilds the committed internal/ui/query-results.html
npm run test:sites

フロントエンドでは React/TypeScript、@modelcontextprotocol/ext-apps、vite-plugin-singlefile を使用します。ビルドすると、すべてがインライン化された HTML ファイルが 1 つ出力され、internal/ui/query-results.html にコピーされます。このファイルは Go に埋め込まれるため、通常の go build に Node は不要です。Dockerfile は Node ステージでフロントエンドを再ビルドし、Go のコンパイル前にコミット済みのバンドルを上書きすることで、リリースイメージに古い UI が含まれるのを防ぎます。

設計ドキュメントは docs/superpowers にあります。各機能には仕様書と実装計画が付属します。

ライセンス

Apache-2.0

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。