Claude Media
MySQL MCPサーバーをClaude Codeで使う — 読み取り専用で接続する手順

MySQL MCPサーバーをClaude Codeで使う — 読み取り専用で接続する手順

Claude CodeにMySQL MCPサーバー(mcp-server-mysql)を1コマンドで登録し、読み取り専用で接続する手順です。書き込み許可のフラグとPIIマスキングまで扱います。

Claude CodeからMySQLを使うには、claude mcp add mcp_server_mysql -- npx @benborla29/mcp-server-mysqlの形でMCPサーバーを登録します。接続情報はMYSQL_HOST・MYSQL_USER・MYSQL_DBなどの環境変数で渡します。書き込み系の環境変数を付けなければ、読み取り専用のままです。全文のコマンドは「接続して動くところまで確認する」にあります。

MySQLの開発元Oracleも、github.com/oracle/mcpでMCPサーバーを公開しています。ただしMySQL AIとMySQL HeatWave向けの概念実証で、本番利用は想定されていません。汎用のMySQLで、環境変数による権限制御とPIIリダクションまで備えているのがbenborla/mcp-server-mysql(npmパッケージ@benborla29/mcp-server-mysql)です。スキーマの確認からクエリの実行までを、既定で読み取り専用のまま扱えます。

汎用DB接続サーバーDBHubを軸にしたMCPからデータベースに接続する方法がすでにあります。DBHubは複数のDB製品を横断する分、MySQL固有の権限設計までは持ちません。この記事ではmcp-server-mysqlの導入手順と、書き込みを許可する環境変数の設計、スキーマ検査の使い方に絞ります。グラフDBのNeo4jはNeo4j MCPサーバーの導入手順にまとめています。

mcp-server-mysqlとDBHubは何が違うか

同じ「MCPでMySQLに触る」でも、狙っている作業の深さが違います。

くらべる

どちらを選ぶか

複数のDBを1本で

DBHub

PostgreSQL・MySQL・MariaDB・SQL Server・Oracle・SQLiteに対応します。Claude Code向けには読み取り専用のプラグインが用意されています。claude mcp addで本体を入れる場合は、readonly = trueの設定で読み取り専用にします。

MySQLを深く

mcp-server-mysql

対象はMySQLだけです。書き込みをINSERT・UPDATE・DELETE・DDLの操作ごとに許可し、データベース単位の権限とPIIマスキングも持ちます。

PostgreSQLとMySQLを同じ手順で触りたいならDBHub、MySQLだけを対象にデータベースごとの権限や個人情報の保護まで踏み込みたいならmcp-server-mysql、という住み分けです。

接続して動くところまで確認する

必要な環境はNode.js v20以上とMySQL 5.7以上(8.0以上を推奨)です。Claude Codeへの登録が最も簡単で、npx経由でパッケージを都度取得します。

手順

接続から動作確認まで

  1. 1

    読み取り専用のMySQLユーザーを作る

    接続に使うユーザーにはSELECT権限だけを与えます。MCPサーバー側のフラグに加えて、DB側の権限でも書き込みを止める二重の防御です。

  2. 2

    claude mcp add で登録する

    環境変数は-eで渡し、サーバー起動コマンドは--の後ろに置きます。書き込み系のフラグは何も付けません。

  3. 3

    接続を確認する

    Claude Codeで/mcpを開くか、claude mcp get mcp_server_mysqlを実行します。

  4. 4

    スキーマを尋ねる

    「テーブルの一覧と、主要テーブルのカラムと型を教えて」と頼み、mysql://tablesの一覧が返るかを見ます。主キーはリソースに含まれないので、SHOW INDEXで確かめます。

登録コマンドは次のとおりです。

claude mcp add mcp_server_mysql \
  -e MYSQL_HOST="127.0.0.1" \
  -e MYSQL_PORT="3306" \
  -e MYSQL_USER="readonly_user" \
  -e MYSQL_PASS="your_password" \
  -e MYSQL_DB="your_database" \
  -- npx @benborla29/mcp-server-mysql

ALLOW_INSERT_OPERATION・ALLOW_UPDATE_OPERATION・ALLOW_DELETE_OPERATIONは指定しなければfalseです。上のコマンドだけで読み取り専用の接続になります。すでにClaude Desktopに設定済みなら、claude mcp add-from-claude-desktopでその設定を取り込めます(MacとWSLのみ)。

登録先は3つのスコープで変わる

claude mcp addはスコープを指定しないとlocalに保存します。保存先は~/.claude.jsonで、現在のプロジェクトだけに効き、チームには共有されません。チームで同じ接続を使うなら--scope projectを付けます。

v2.1.287で、使い捨てのディレクトリに--scope projectで登録すると、次の出力と.mcp.jsonができました(パスワードは渡していません)。

claude mcp add -s project mcp_server_mysql \
  -e MYSQL_HOST=127.0.0.1 -e MYSQL_USER=readonly_user -e MYSQL_DB=your_database \
  -- npx @benborla29/mcp-server-mysql
Added stdio MCP server mcp_server_mysql with command: npx @benborla29/mcp-server-mysql to project config
File modified: <作業ディレクトリ>/.mcp.json
{
  "mcpServers": {
    "mcp_server_mysql": {
      "type": "stdio",
      "command": "npx",
      "args": ["@benborla29/mcp-server-mysql"],
      "env": {
        "MYSQL_HOST": "127.0.0.1",
        "MYSQL_USER": "readonly_user",
        "MYSQL_DB": "your_database"
      }
    }
  }
}

-eに渡した値は、そのまま.mcp.jsonのenvに平文で書き込まれます。MYSQL_PASSを-eで渡してプロジェクトスコープに登録すると、パスワードがリポジトリに入ります。.mcp.jsonでは${VAR}の形で環境変数を展開できるので、envのMYSQL_PASSを"${MYSQL_PASS}"と書き換え、実際の値は各自のシェルに持たせる構成が取れます。展開先はcommand・args・env・url・headersです。

また、.mcp.jsonのサーバーは承認が済むまで接続されません。v2.1.287のclaude mcp listとgetのヘルプには「承認前のサーバーはPending approvalと表示され、接続しない」とあります。承認はclaudeを対話で起動して行います。v2.1.196以降は、ワークスペースの信頼ダイアログを受け入れるまで、リポジトリにコミットした承認設定は読まれません。

書き込みを許可する範囲をどう決めるか

mcp-server-mysqlにはREADONLY=trueのような単一トグルがありません。書き込みの種類ごとに、個別の環境変数で許可します。

変数既定値効果
ALLOW_INSERT_OPERATION既定値false効果INSERTを許可するか
ALLOW_UPDATE_OPERATION既定値false効果UPDATEを許可するか
ALLOW_DELETE_OPERATION既定値false効果DELETEを許可するか
ALLOW_DDL_OPERATION既定値false効果CREATE/DROP/ALTERを許可するか
MYSQL_DISABLE_READ_ONLY_TRANSACTIONS既定値false効果読み取り専用トランザクションの強制を外すか

上4つがすべてfalseなら、mysql_queryツールはSELECT系のクエリしか通しません。MYSQL_DISABLE_READ_ONLY_TRANSACTIONSの既定はfalseで、読み取り専用トランザクションの強制が有効です。ドキュメントはこの変数に警告記号を付けており、trueにすると二段目の防御が外れます。

データベースごとに権限を出し分けたいときは、スキーマ単位の権限設定を使います。

SCHEMA_INSERT_PERMISSIONS=development:true,test:true,production:false
SCHEMA_UPDATE_PERMISSIONS=development:true,test:true,production:false
SCHEMA_DDL_PERMISSIONS=development:false,test:true,production:false
SCHEMA_DELETE_PERMISSIONS=development:false,test:true,production:false

この設定なら、開発用DBには書き込みを許しつつ、本番DBは読み取り専用のまま1つのMCPサーバーで運用できます。ここで指定した値は、グローバルのALLOW_*より優先されます。

複数のデータベースを横断して調べる

MYSQL_DBを省略すると、マルチDBモードで起動します。1つのMySQLインスタンスに複数サービスのDBが同居している環境で、接続を切り替えずに全体を見渡せます。

claude mcp add mcp_server_mysql_multi \
  -e MYSQL_HOST="127.0.0.1" \
  -e MYSQL_PORT="3306" \
  -e MYSQL_USER="readonly_user" \
  -e MYSQL_PASS="your_password" \
  -e MULTI_DB_WRITE_MODE="false" \
  -- npx @benborla29/mcp-server-mysql

マルチDBモードは書き込みが既定で止まります(MULTI_DB_WRITE_MODE=false)。解除したいデータベースだけを、先ほどのSCHEMA_*_PERMISSIONSで指定します。クエリではdatabase.tableの形にスキーマ名を付けます。付けないと、どのデータベースのテーブルか曖昧になります。

接続後にテーブル構成を尋ねると、mysql://tablesでテーブルの一覧が、mysql://tables/<テーブル名>で各カラムの名前と型が返ります。NULL許可やキーまで知りたいときは、DESCRIBEやSHOW INDEXをmysql_queryで実行させます。「先月作成された注文の件数をステータス別に数えて」のような集計も、mysql_queryがSELECT文を組み立てて実行します。

個人情報をマスキングしながら調べる

ENABLE_PII_REDACTION=trueにすると、クエリ結果の個人情報がマスキングされます。検出は列名と値の両方で行われます。

列名は、email・ssn・phone・first_name・address・credit_card・password・api_key・tokenなどの組み込みリストで照合します。値は、メールアドレス・米国形式の電話番号・SSN・IPv4・Luhn検査を通るクレジットカード番号を正規表現で見つけます。マスクの形は次のとおりです。

入力出力
jane.doe@example.com出力j***@e***.com
415-555-0134出力***-***-0134
4111 1111 1111 1111出力****-****-****-1111
列名が一致した一般の値出力J********(先頭1文字と最大8個の*)

検出対象はPII_EXTRA_COLUMNS(列名の部分一致をカンマ区切りで指定)とPII_EXTRA_COLUMN_PATTERNS(正規表現をセミコロン区切りで指定)で足せます。ENABLE_PII_REDACTION=trueのときだけ効き、組み込みリストへの追加になります。

有効にすると既定で拒否されるクエリ

PIIリダクションを有効にすると、実行前の検査が3つ加わります。どれも既定はfalse、つまり拒否する側です。

既定値

PII保護を有効にした直後の挙動

  • SELECT * を拒否

    SELECT *とt.*を弾き、列の明示を求めます。COUNT(*)などの集計は対象外です。許可するにはPII_ALLOW_SELECT_STAR=trueにします。

  • PII列への参照を拒否

    射影・WHERE・JOIN・GROUP BY・サブクエリなど、PII列と判定された列を含むクエリを弾きます。PII_ALLOW_REFERENCES=trueで解除できます。

  • スキーマ参照を制限

    PII_ALLOW_INTROSPECTIONがfalseのとき、DESCRIBEなどはPII列の行を除いて返します。SHOW CREATE TABLEは拒否されます。

PII_ALLOW_INTROSPECTIONは「スキーマ参照を一切許さない」設定ではありません。falseのとき、SHOW COLUMNS・DESCRIBE・SHOW INDEXはPII列の行を落として実行されます。SHOW TABLES・SHOW DATABASESはそのまま通り、SHOW CREATE TABLEとinformation_schema・mysqlへのSELECTは拒否されます。

SHOW CREATE TABLEの代わりには、mysql://tables/<テーブル名>リソースが案内されています。PIIリダクションが有効なら、このリソースはPII列をスキーマ応答から隠します。

PII列の存在そのものを見せたくないなら、PII_BLOCK_INTROSPECTION=trueにします。SHOW TABLESを含む参照系の文がすべて止まります。PII_ALLOW_INTROSPECTION=trueのときは無視されます。

マスキングが効かないケース

READMEにある既知の制限を知らないまま使うと、過信につながります。

  • BIGINT・INTに入れた電話番号やSSNは、文字列ではないのでマスクされません
  • JSON列の中のメールや電話はマスクされますが、first_nameのような入れ子の項目名は検査されません
  • IPv6、日本を含む米国外の電話番号(E.164形式)、IBAN、各国の身分証番号は対象外です
  • Luhn検査に通らない13〜19桁の数字列は、注文IDを壊さないよう意図的に残されます
  • PII_ALLOW_REFERENCES=trueにすると、SELECT first_name AS fnのような別名で列名の照合をすり抜けます

日本の住所・氏名・マイナンバーを扱うデータベースでは、組み込みの値パターンがほとんど効かないことになります。列名の照合とPII_EXTRA_COLUMNSで、日本語の業務で使う列名を足す運用が現実的です。

本番DBに負荷をかけない安全弁

書き込みを止めても、重い読み取りクエリは本番DBの負荷になります。調整できる項目は次のとおりです。

変数既定値効果
MYSQL_POOL_SIZE既定値10効果コネクションプールの上限
MYSQL_QUERY_TIMEOUT既定値30000効果クエリのタイムアウト(ミリ秒)
MYSQL_RATE_LIMIT既定値100効果1分あたりに許可するクエリ数
MYSQL_MAX_QUERY_COMPLEXITY既定値1000効果クエリの複雑さスコアの上限
MYSQL_CACHE_TTL既定値60000効果キャッシュの保持時間(ミリ秒)
MYSQL_QUEUE_LIMIT既定値100効果待たせる接続要求の上限
MYSQL_CONNECT_TIMEOUT既定値10000効果接続確立のタイムアウト(ミリ秒)

MYSQL_RATE_LIMITとMYSQL_MAX_QUERY_COMPLEXITYは、Claudeが会話の中で次々にクエリを投げる状況への歯止めです。クエリが拒否されたときは、トラブルシューティングの節にもMYSQL_RATE_LIMITの確認が挙がっています。複雑なJOINを多用する分析ではMYSQL_MAX_QUERY_COMPLEXITYを、クエリが遅いときはMYSQL_POOL_SIZEとMYSQL_QUERY_TIMEOUTを見ます。

履歴を残したい場合はMYSQL_ENABLE_LOGGING=trueにします。ログレベルはMYSQL_LOG_LEVEL(既定info)です。性能メトリクスはMYSQL_METRICS_ENABLED=trueで収集できます。

接続経路を選ぶ

基本のホスト・ポート指定のほかに、次の経路があります。

  • Unixソケット: MYSQL_SOCKET_PATHを指定します。指定するとホストとポートは無視されます
  • 接続文字列: MYSQL_CONNECTION_STRINGにmysqlコマンド形式の文字列を渡します。個別の変数より優先され、認証情報を入れ替えたい場面に向きます
  • SSLとmTLS: MYSQL_SSL=trueとMYSQL_SSL_CAを指定します。クライアント証明書で相互認証するならMYSQL_SSL_CERTとMYSQL_SSL_KEYも渡します
  • SSHトンネル: READMEの機能一覧にあり、リモートのDBに繋ぐ用途を想定しています
  • リモートMCP: IS_REMOTE_MCP=trueでHTTPサーバーモードになり、REMOTE_SECRET_KEYのベアラートークンで認証します。待ち受けポートの既定は3000です

MYSQL_TIMEZONEで日時のタイムゾーンを固定でき、+08:00・Z・localなどが指定できます。アプリケーション側の時刻表示とずれるときに使います。

接続できない・書き込めないときの切り分け

症状ごとに見る場所が違います。

症状見る場所
Could not connect to MCP server見る場所envにPATHを足し、Node.jsのbinディレクトリを指定する(which nodeで確認)
Server disconnected見る場所Claude Desktopのログ~/Library/Logs/Claude/mcp-server-mcp_server_mysql.logを見て、Node.jsとスクリプトを絶対パスで指定する
MySQL 8.0以降で認証に失敗する見る場所caching_sha2_passwordへの対応を確認する。合わなければIDENTIFIED WITH mysql_native_passwordでユーザーを作り直す
ERR_MODULE_NOT_FOUND: Cannot find package 'dotenv'見る場所npx -y -p @benborla29/mcp-server-mysql -p dotenv mcp-server-mysqlで起動する
書き込みが通らない見る場所ALLOW_*のフラグ、SCHEMA_*_PERMISSIONS、MySQLユーザーのGRANTを順に確認する

最後の行では、MCP側のフラグとDB側の権限が両方通って初めて書き込めます。フラグをtrueにしても、MySQLユーザーにINSERT権限がなければ失敗します。逆に、DB側が広い権限でもフラグがfalseなら止まります。

運用上の注意

  • READONLYという単独の環境変数は存在しません。読み取り専用にするにはALLOW_*系を設定しない(すべてfalse)ままにします
  • MYSQL_DISABLE_READ_ONLY_TRANSACTIONSをtrueにすると、ALLOW_*がfalseでも二段目の防御が外れます
  • IS_REMOTE_MCP=trueにするとネットワーク越しにアクセスできます。REMOTE_SECRET_KEYの管理とアクセス元の制限が、ローカル運用より重くなります
  • 設計の考え方はMCPで本番環境に接続する権限設計とデータベースMCPサーバーの読み取り専用設定の比較に書いています

まとめ

mcp-server-mysqlはフラグ4つとスキーマ単位の権限で、書き込みの許可範囲を細かく決められます。PIIリダクションは「SELECT*を弾く」「PII列を含むクエリを弾く」が既定なので、日本語の列名や数値型の個人情報が混じるDBでは、マスキングに頼らず列の公開範囲を絞る必要があります。PostgreSQL側の同種の選択肢はPostgreSQL MCPサーバーの使い方にあります。まず個人の開発DBに既定の読み取り専用設定で繋ぎ、動作を確かめてから本番相当の権限設計に進むと、手戻りが少なくなります。

この記事を共有:XはてブLinkedIn