$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

MySQLのスキーマ情報を圧縮して提供するMCPサーバーを作った

AIを使ってMySQLのクエリを書くときは、自分が関わっているプロジェクトのスキーマ情報を読み込ませることが必須である。ただし超巨大プロジェクトに携わっていると、特定データベースのテーブル数が数百規模になってしまう。この場合スキーマのダンプ情報を読み込ませるとコンテキスト長が非常に長くなってしまい、失敗することが多かった。

この問題を解決するため、MCPを使ってスキーマ情報をいい感じに圧縮して返すことでSQL生成がもっとうまくいくのではないかと考えた。そこで、テーブル一覧と詳細情報を段階的に取得できるMCPサーバーを、MCPの勉強がてら作ってみた。

作ったもの

https://github.com/shibayu36/mysql-schema-explorer-mcp

次のように動く。

実際に大きめなプロジェクトで試したところ、いつもよりうまく生成できるようになった気がする(仕事のプロジェクトだし言語化が非常に難しいが...)

課題とアイデア

たとえば以下のようなスキーマがあったとすると、この4テーブルだけでもトークンサイズが400を超えてしまう。これが数百テーブルになると、コンテキスト長が数万になってしまう。

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'ユーザーシステムID',
    email VARCHAR(255) NOT NULL UNIQUE COMMENT 'メールアドレス',
    username VARCHAR(255) NOT NULL UNIQUE COMMENT 'ユーザー名',
    tenant_id INT NOT NULL COMMENT 'テナントID',
    employee_id INT NOT NULL COMMENT '従業員ID',
    UNIQUE KEY uk_tenant_employee (tenant_id, employee_id)
) COMMENT='ユーザー情報';

CREATE TABLE orders (
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '注文ID',
    user_id INT NOT NULL COMMENT 'ユーザーID (FK)',
    order_date DATETIME COMMENT '注文日時',
    INDEX(id),
    FOREIGN KEY fk_user (user_id) REFERENCES users(id)
) COMMENT='注文ヘッダー';

CREATE TABLE products (
    product_code VARCHAR(50) PRIMARY KEY COMMENT '商品コード(主キー)',
    maker_code VARCHAR(50) NOT NULL COMMENT 'メーカーコード',
    internal_code VARCHAR(50) NOT NULL COMMENT '社内商品コード',
    product_name VARCHAR(255) COMMENT '商品名',
    UNIQUE KEY uk_maker_internal (maker_code, internal_code),
    INDEX idx_product_name (product_name),
    INDEX idx_maker_product_name (maker_code, product_name)
) COMMENT='商品マスター';

CREATE TABLE order_items (
    order_id INT NOT NULL COMMENT '注文ID (FK)',
    item_seq INT NOT NULL COMMENT '注文明細連番',
    product_maker VARCHAR(50) NOT NULL COMMENT '商品メーカーコード (FK)',
    product_internal_code VARCHAR(50) NOT NULL COMMENT '商品社内コード (FK)',
    quantity INT NOT NULL COMMENT '数量',
    PRIMARY KEY (order_id, item_seq),
    UNIQUE KEY uk_order_product (order_id, product_maker, product_internal_code),
    FOREIGN KEY fk_order (order_id) REFERENCES orders(id) ON DELETE CASCADE,
    FOREIGN KEY fk_product (product_maker, product_internal_code) REFERENCES products(maker_code, internal_code)
) COMMENT='注文明細'; 

実際にSQLを生成するときは、スキーマのダンプ情報の全ては必要ないはずである。そこで以下のアイデアで圧縮して返したいと考えた。

  • (1) テーブル一覧として、テーブル名/コメント/PRIMARY KEY/UNIQUE KEY/FOREIGN KEYを返せば、テーブルやテーブルの関係性をだいたい把握できる
  • (2) さらに追加で情報が欲しくなった時に、指定したテーブルの詳細を取得することで全フィールド情報を取得できたら良い

これを実現するために、(1)をlist_tables toolとして、(2)をdescribe_table toolとして、MCPサーバーを作ることにした。

実際に上記構造をMCPサーバーで返した内容はこちら。

(1)のlist_tables tool出力。146トークン

データベース「ecshop」のテーブル一覧 (全4件)

- order_items - 注文明細 [PK: (order_id, item_seq)] [UK: (order_id, product_maker, product_internal_code)] [FK: order_id -> orders.id; (product_maker, product_internal_code) -> products.(maker_code, internal_code)]
- orders - 注文ヘッダー [PK: id] [FK: user_id -> users.id]
- products - 商品マスター [PK: product_code] [UK: (maker_code, internal_code)]
- users - ユーザー情報 [PK: id] [UK: email; (tenant_id, employee_id); username]

(2)のdescribe_table tool出力。usersとordersを指定したときの出力。188トークン

# テーブル: users - ユーザー情報

## カラム
- id: int NOT NULL [ユーザーシステムID]
- email: varchar(255) NOT NULL [メールアドレス]
- username: varchar(255) NOT NULL [ユーザー名]
- tenant_id: int NOT NULL [テナントID]
- employee_id: int NOT NULL [従業員ID]

## キー情報
[PK: id]
[UK: email; (tenant_id, employee_id); username]

---

# テーブル: orders - 注文ヘッダー

## カラム
- id: int NOT NULL [注文ID]
- user_id: int NOT NULL [ユーザーID (FK)]
- order_date: datetime NULL [注文日時]

## キー情報
[PK: id]
[FK: user_id -> users.id]
[INDEX: user_id; id]

まとめ

今回はMySQLのスキーマ情報を圧縮して提供するMCPサーバーを作ってみた。これを使ってみると、一応なんとなくSQLの生成の精度が良くなった気がする。

このサーバーの開発ではVibecodingを試してみたので、その辺りの開発日誌はまた書こうと思う。