はじめに
この連載で何度か紹介してきた自作ツール vba_manager.py(GitHub で公開中)を大きく更新したので、その告知です。あわせて、使ってみて分かった「これで何が変わったのか」の話をします。
もともとこのツールは、Excel の VBA コードを管理するためのものでした。マクロの一覧を出す、コードを取り出す、直して戻す。コマンドにして 9 個、それだけの道具でした。
今回の更新で、シートそのものを操作する機能を足して、約 50 コマンドになりました。
きっかけは Excel MCP
きっかけは、ステファンさん(Stefan Broenner さん)が公開している Excel MCP(sbroenne/mcp-server-excel)を触ったことです。COM 経由で Excel を操作する MCP サーバーで、範囲編集・書式・グラフ・ピボット・PowerQuery まで一通りそろっています。よくできています。
ただ、Excel MCP は「対象ファイルは Excel で閉じておく」前提の設計になっています。COM の排他制御を考えれば確実で正しい判断だと思います。一方、私は秀.xlsm をアドインとして常時開いていますし、作業ファイルも開きっぱなしで触る流儀です。
そこで、Excel MCP の機能を参考にしながら、「開いたままのブック」を対象に同じようなことができる機能を、Claude Code との会話で vba_manager.py に足していきました。vba_manager.py はもともと GetActiveObject で起動中の Excel に横から接続する作りなので、土台はそのまま使えました。
何を足したか
大きく三段階あります。
読む系(AIの目) ── シートの状態をテキストや画像で確認する
py vba_manager.py read-range A1:D10 # セル値をテキスト格子で
py vba_manager.py sheet-info # シート構成・使用範囲
py vba_manager.py screenshot A1:H30 # 範囲をPNGに
書く系(AIの手) ── 値・書式・行列・検索置換・印刷設定など
py vba_manager.py write-range C1 "=SUM(A1:A10)"
py vba_manager.py format-range A1:D1 --bold --bg "#FFFF00"
py vba_manager.py find-replace 旧 新 A1:Z99
重量級 ── グラフ・ピボット・スライサー・PowerQuery・データモデル
py vba_manager.py chart create A1:B5 --type column --title "月別売上"
py vba_manager.py pivot create 元データ!A1:C100 --rows 部門 --values 売上
py vba_manager.py powerquery load 商品マスタ --to sheet
ファイルパスを省略すると、いま Excel で開いているアクティブなブックが自動的に対象になります。
一番変わったのは「会話の中身」
機能一覧より、こちらが本題かもしれません。
この連載の第1回は「会話するだけでマクロが直る」という話でした。あれは今でも本当です。ただ、あの頃の AI が見ていたのはコードだけでした。コードを読み、コードの理屈で直す。マクロが実際に操作するシートがどうなっているかは、私が言葉で説明するしかありませんでした。
目と手が付くと、ここが変わります。実際にあった例を一つ。
先日、公開している「ファイル一覧」(フォルダ配下のファイルを Excel に一覧化する自作ツール)のマクロを AI にレビューさせました。検索マクロの中に、こういう行があります。
wsTarget.Range("A1").AutoFilter Field:=1, Criteria1:="*" & CTV & "*"
コードだけ読めば、これは A 列を検索しているように見えます。ところがこのシートの A 列は連番で、ファイル名は B 列です。つまり「検索ワードを番号列に当てている=このマクロは壊れている」という指摘になるはずでした。
AI は指摘する前に、シートを見ました。sheet-info でシート構成を確認し、ヘッダー行を読み、さらに実際にフィルタをかけて挙動を測りました。すると、シートには既にオートフィルタが B 列起点(B1:F)で張られていて、Field:=1 は B 列=ファイル名に効いていることが分かりました。7.8 万行の実データに "md" でフィルタすると 1,773 件。マクロは正しく動いていました。
補足:Field:=1 がなぜ B 列に効くのか
AutoFilter の Field は、シートの列番号ではなくフィルタ範囲の左端から数えた相対番号です。
| シートの列 | A(番号) | B(ファイル名) | C(フォルダ) | D(更新日時) |
|---|---|---|---|---|
| フィルタ範囲 B1:F の中では | ―(範囲外) | Field 1 | Field 2 | Field 3 |
このシートには既にフィルタが B 列起点(B1:F) で張られていたため、Field:=1 は
A 列ではなく B 列=ファイル名に効いていました。つまりこのコードが正しいかどうかは、
コードだけでは決まらず、いまシートにどんな範囲でフィルタが張られているかで決まります。
だから「実物を見てから物を言う」が効いた、という話です。
結果、指摘はこうなりました ──「バグではない。ただし既存フィルタへの暗黙の依存なので、誰かがフィルタを解除すると検索が静かに 0 件になる。範囲を明示した方がいい」。修正後は、AI が自分でその検索マクロを実行して、同じ 1,773 件が出ることまで確認してから「直りました」と言ってきました。
コードしか見えない AI なら、正常なコードを「バグ」と直して本当のバグを作ったか、何も気づかず素通りしたか、どちらかだったと思います。「コードとして正しいか」ではなく「実物と整合しているか」を確かめてから物を言うようになった、ということです。コードの中の Offset(0, 4) が何を指すかも、シートを見れば一目で分かります(B列のファイル名から4つ右=F列のパス)。会話するだけでマクロが直る、の「会話」の中身が、別物に濃くなりました。
Excel MCP との棲み分け
競合ではなく使い分けの話です。
- 閉じたファイルを確実に一括処理する → Excel MCP
- 開いたままのブックをその場で見て・触って・確かめる → vba_manager.py
開いたままを触る方式には引き換えもあります。プログラム経由の変更は Excel の Undo 履歴を消します。なので vba_manager の編集系コマンドは既定では保存しません。Excel の画面で確認して、良ければ保存、ダメなら保存せずに閉じれば変更を破棄できます。Undo の代わりはそうやって確保しています。
安全まわり
地味な部分ですが、今回の更新で一緒に入れたものです。
- 置換系コマンドは実行前に自動バックアップを取ります(ブック全体+モジュール単位の二段)
- .bas ファイルの文字コード事故(CP932 を UTF-8 で上書きして日本語が壊れるやつ)を、インポート前に機械的に検知して止めます
- Excel が未起動の状態でパス指定すると自動化用の Excel が新しく立ち上がりますが、これにはアドインや PERSONAL.XLSB が読み込まれません。普段使いには手動起動した Excel を使ってください(ツールが警告を出します)
おわりに
使い方の詳細は README にまとめてあります。VBA のコード管理だけが目的なら今までどおり 9 個のコマンドで足りますし、そこから先はぶら下がっているだけなので、邪魔にはならないはずです。