メール配信を運用していると、「登録確認メールが届かない」「パスワード再設定のメールが来ない」という問い合わせが必ず来ます。調べる場所は決まっていて、配信ログを宛先で引き、ステータスと応答コードを見て、相手に何を確認してもらうかを決めるだけです。ただ、手順が担当者の頭の中にしかないと、人によって調べる深さが変わり、回答の質もばらつきます。
本記事では、この調査をblastengine MCPサーバーとClaude Codeのスキル(Agent Skills)で定型化します。「/mail-trace 宛先アドレス」と打てば、配信ログを調べて社内向けの調査メモと顧客向けの回答案が出てくる状態がゴールです。あわせて、一斉配信のCSV取り込みが一部失敗したときの調査も同じ形で用意します。
どちらも参照系ツールだけで完結し、メールは1通も送りません。blastengine MCPの導入は導入記事、各ツールの入出力は全15ツールの検証記事を参照してください。手順はblastengine MCP v0.2.2とClaude Code 2.1.284で、実際の配信ログを相手に確認しています。
運用・保守でMCPが効く場面
定期レポートのような「毎週同じものを出す」作業は、非対話モードでバッチにしてしまうのが向いています。一方、問い合わせ対応は毎回入力が違い、調べた結果によって次に見る場所が変わります。人が手でやると、管理画面の配信ログ検索、配信詳細、メールログ詳細を行き来することになります。
この「入力ごとに分岐する調査」は、エージェントに任せると楽になる典型です。ただし毎回プロンプトを書いていては、結局やり方がばらつきます。そこで調査手順をスキルとして書き残し、呼び出し方だけを覚えればよい形にします。
| 問い合わせ | 使うツール | スキル |
|---|---|---|
| メールが届かない |
mail_results_list / mail_log_get / delivery_get
|
/mail-trace |
| CSVで取り込んだ宛先が足りない |
bulk_import_status / bulk_import_error_download
|
/import-trace |
準備:調査専用のMCP設定を作る
調査に送信は要りません。送信系のフラグはすべてfalseにした設定を、調査用のディレクトリに置きます。
{
"mcpServers": {
"blastengine": {
"type": "stdio",
"command": "node",
"args": ["/absolute/path/to/blastengine-mcp/dist/index.js"],
"env": {
"BLASTENGINE_LOGIN_ID": "${BLASTENGINE_LOGIN_ID}",
"BLASTENGINE_API_KEY": "${BLASTENGINE_API_KEY}",
"BLASTENGINE_ENABLE_SEND": "false",
"BLASTENGINE_ENABLE_BULK": "false",
"BLASTENGINE_ENABLE_CSV_IMPORT": "false"
}
}
}
}
このフラグ構成でも、配信ログの検索・取得、配信詳細、CSVインポートの状況確認とエラーファイルのダウンロードはすべて使えます。一斉配信のプレビュー(bulk_preview)だけはBLASTENGINE_ENABLE_BULK=trueが必要ですが、今回の調査では使いません。
ディレクトリ構成は次のとおりです。スキルはプロジェクトの.claude/skills/<スキル名>/SKILL.mdに置くと、そのディレクトリで起動したClaude Codeから/スキル名で呼べます。
mail-ops/
├── mcp.json
├── out/ # エラーCSVの保存先
└── .claude/
└── skills/
├── mail-trace/SKILL.md
└── import-trace/SKILL.md
スキル1:不達調査(/mail-trace)
調査の考え方
blastengineの配信ログでは、宛先ごとに次のステータスが付きます。調査はこれを起点に分岐します。
| ステータス | 意味 | 次に疑うところ |
|---|---|---|
SENT |
配信成功(本記事の事例では応答コード250) | 迷惑メールフォルダ、受信側のフィルタ、転送設定 |
SOFTERROR / RETRY
|
一時的な失敗 | メールボックス容量、相手サーバーの一時拒否 |
HARDERROR |
恒久的な失敗 | アドレスの誤り、廃止されたアドレス |
DROP |
blastengineが送らなかった | 応答メッセージに書かれた理由 |
| ログなし | 指定した条件ではログが見つからない | 検索したアドレスの表記と期間、アプリ側の送信記録 |
最後の「ログなし」が意外に重要です。検索の条件が正しいのにログが無ければ、blastengineに送信依頼が届いていない可能性が高く、調べる先はアプリ側になります。ただし、0件は「その条件では見つからなかった」というだけです。emailでの検索は完全一致で、試したところqa-unregistered(途中まで)もQA-Unregistered@example.com(大文字違い)も0件でした。問い合わせで伝えられたアドレスの表記が、実際に送った宛先と同じとは限りません。期間が保持期間(62日)を過ぎている場合も0件になります。
SKILL.md
---
name: mail-trace
description: 「メールが届かない」という問い合わせを受けたときに、blastengineの配信ログから宛先アドレス単位で原因を調べ、社内向けの調査メモと顧客向けの回答案を作る。引数は宛先メールアドレスと、分かれば送信日(例:user@example.com 2026-08-26)。
---
# 不達調査(blastengine)
引数: $ARGUMENTS
blastengine MCPの参照系ツールだけを使う。送信・一斉配信・インポート系のツールは呼ばない。
## 手順
1. 調査期間を決める。送信日が分かっていればその日の00:00〜23:59(+09:00)、分からなければ今日から遡って14日間。日時は必ずオフセット付きISO 8601にする。配信ログは62日で消えるため、それより古い日付なら調査不能と報告して終える。
2. `blastengine_mail_results_list`を`email`で絞って呼ぶ。`status`は`ALL`を指定する。
3. 該当が0件なら「指定した条件では配信ログを確認できなかった」と書く。`email`は完全一致で、大文字・小文字の違いでも0件になる。問い合わせのアドレスの表記、検索期間、保持期間(62日)を確認したうえで、それでも無ければ、アプリ側の送信記録(送信処理が動いたか、実際に渡した宛先)の確認を依頼事項として書く。送信依頼が無かったと断定はしない。
4. 該当があれば、新しいものから最大3件について`blastengine_mail_log_get`で`sent_history`を取る。
5. ステータスごとに判定する。
- SENT … 配信成功。応答コードと`sent_history`も確認して書く。迷惑メールフォルダ、受信側のフィルタ、転送設定を疑う。
- SOFTERROR / RETRY … 一時的な失敗。メールボックス容量超過や相手サーバーの一時拒否。`sent_history`の再送経過を書く。
- HARDERROR … 恒久的な失敗。アドレスの誤り・廃止を疑う。応答コードとメッセージをそのまま引用する。
- DROP … blastengineが送信しなかった。応答メッセージに理由が書かれているので引用する。
6. 同じ配信の他の宛先も失敗しているかを見る。`blastengine_delivery_get`で`drop_count` / `hard_error_count` / `soft_error_count`を確認し、この宛先だけの問題か、配信全体の問題かを切り分ける。
## 注意
- `blastengine_delivery_get`が返す本文は差し込み前のテンプレート。`__name__`などが残っていても、未置換で送られた証拠にはならない。この点には触れない。
- 顧客向け回答案では、DROPやログなしのように相手に届いていないケースで迷惑メールフォルダの確認を頼まない。
## 出力
次の2つを作る。推測は推測と明記する。
### 社内向け調査メモ
- 対象アドレス、調査期間
- 該当ログ(配信ID、配信日時、件名、ステータス、応答コード、応答メッセージ)
- 判定と根拠
- 次にやること
### 顧客向け回答案
- 敬語で5〜8行。応答コードや内部の配信IDは書かない。
- 相手に確認してほしいこと(迷惑メールフォルダ、アドレスの綴り、受信拒否設定など)を具体的に書く。
descriptionは、Claudeがスキルを選ぶときの手がかりになります。引数の形を例つきで書いておくと、呼び出す人も迷いません。本文中の$ARGUMENTSは、/mail-traceの後ろに書いた文字列に置き換わります。
「注意」の2項目は、最初の版には無く、試しに動かした結果を見て足したものです。理由は後の「動かして分かったこと」で説明します。
実行する
対話で使うなら、調査用ディレクトリでclaude --mcp-config mcp.jsonを起動して、次のように打ちます。
/mail-trace user@example.com 2026-08-26
非対話で回す場合は、参照系ツールだけを許可します。
claude -p "/mail-trace user@example.com 2026-08-26" \
--mcp-config mcp.json --strict-mcp-config \
--allowedTools "mcp__blastengine__blastengine_mail_results_list,mcp__blastengine__blastengine_mail_log_get,mcp__blastengine__blastengine_delivery_get,Skill" \
< /dev/null
Skillはスキルを読み込むためのツールで、非対話では許可リストに入れておきます。
結果1:DROPのケース
トライアルアカウントでは、登録していないアドレス宛てのメールは送信されません。この状態で送った1通を調べさせた結果が次です(抜粋)。
## 社内向け調査メモ
対象: qa-unregistered@example.com
調査期間: 2026-08-26 00:00〜23:59 (+09:00)。配信ログの保持期限(62日)内。
| 配信ID / ログID | 1264 / 1281 |
| ステータス | DROP |
| 応答コード | 554(rejection) |
| 応答メッセージ | 「宛先が配信可能なアドレスではありません。配信可能アドレスの登録状況を
ご確認ください。…」 |
判定と根拠
- blastengineが送信しなかったケース(DROP)です。相手サーバーには到達していません。
- 配信IDが1264の配信は宛先1件のみで、drop_count=1、hard_error_count=0、
soft_error_count=0です。
応答メッセージをそのまま引用しているので、原因が一目で分かります。5ターン、約0.11ドル(Sonnet)でした。
ここで1つ、運用上の落とし穴があります。この配信1264は、配信単位で見るとステータスがSENTです。
{
"delivery_id": 1264,
"status": "SENT",
"total_count": 1,
"sent_count": 0,
"drop_count": 1
}
配信のステータスは「配信処理が終わった」という意味で、全員に届いたという意味ではありません。配信一覧のSENTだけを見て「送れている」と判断すると、この1件を見落とします。宛先単位のログか、drop_countなどの件数まで見る必要があります。スキルの手順6は、この確認のために入れています。
結果2:SENTのケース
SENT(この事例では応答コード250)のケースでは、次のように受信側の確認事項が並びます(顧客向け回答案の抜粋)。
弊社で送信記録を確認したところ、2026年8月27日 午前8時50分頃と51分頃に、
貴社アドレス宛てのメールが送信されており、相手先のメールサーバーでも正常に
受け付けられていることを確認しました。
そのため、お手数ですが受信側の状況をご確認いただけますでしょうか。
・迷惑メールフォルダ、ゴミ箱、「すべてのメール」フォルダに、上記時間帯のメールが入っていないか
・受信拒否設定や、特定ドメインのみ受信する設定になっていないか
・転送設定により、別のアドレスへ転送されていないか
「送った」ではなく「相手のサーバーが受け取った」と書き分けているのは、手順5の判定基準をそのまま使っているためです。
結果3:ログが無いケース
一度も送っていないアドレスを渡した結果です(抜粋)。
対象: nobody@skillogy-example.jp
期間: 2026-09-16〜2026-09-30(送信日が不明なので直近14日)。追加で2026-08-01〜2026-09-30
も検索しました。62日の保持期間内でほぼ全域です。
条件: email完全一致、statusはALL
判定と根拠
- 指定した条件では、blastengineの配信ログを確認できませんでした。
- 原因は特定できていません。以下はいずれも推測です。
- 問い合わせのアドレスと実際の送信先の表記が違う可能性があります。emailは完全一致
なので、大文字・小文字の違いでも0件になります。
- 送信日が8/1より前なら、保持期間(62日)を過ぎてログが消えている可能性があります。
- アプリ側でblastengineに送信依頼が渡っていない可能性があります。
- 送信依頼が無かったと断定はできません。
4ターン、約0.11ドルでした。
最初の版の手順3は「0件なら送信依頼が届いていないと判断する」でした。このときの出力は「blastengineに、このアドレス宛の送信依頼が届いていません」と言い切っていました。0件から分かるのは「その条件では見つからなかった」ことだけなので、手順3を直しています。直したあとは、送信日が分からないときに自分で期間を広げて検索し直すところまで進みました。
スキル2:CSV取り込み失敗の調査(/import-trace)
一斉配信でCSVから宛先を取り込むと、形式の誤ったアドレスは取り込まれずに残ります。件数が合わないときの調査も、同じようにスキルにしておきます。
---
name: import-trace
description: blastengineの一斉配信でCSV宛先インポートが一部失敗したときに、ジョブIDから失敗件数と失敗理由を集計し、元のCSVのどこを直せばよいかをまとめる。引数はインポートのジョブID(例:54)。
---
# CSVインポート失敗の調査(blastengine)
引数: $ARGUMENTS
## 手順
1. `blastengine_bulk_import_status`をジョブIDで呼び、`status`、`total_count`、`success_count`、`failed_count`を控える。`status`がFINISHEDでなければ、完了待ちであることを報告して終える。
2. `failed_count`が0なら、失敗なしと報告して終える。
3. `blastengine_bulk_import_error_download`でエラーCSVを保存する。`output_path`は`./out/import-errors-<ジョブID>-<YYYYMMDDHHmmss>.zip`(既存ファイルは上書きできないため、毎回新しい名前にする)。MCPはファイルの中身を返さないので、保存後に`unzip -p <zipのパス>`で読む。コマンドはパイプや`;`でつながず、単独で実行する(許可ルールに一致させるため)。
4. エラーCSVは「エラーメッセージ」「email」の2列(UTF-8、BOM付き)。エラーメッセージの種類ごとに件数を数える。
## 出力
- ジョブの集計(総数・成功・失敗)
- 失敗理由ごとの件数と、該当アドレスの例(最大5件。アドレスは先頭3文字以外を伏せる)
- 元CSVの直し方(どの列をどう直すか)
- 直したCSVだけを再インポートする場合の注意(成功済みの宛先を二重登録しない)
ポイントは手順3です。bulk_import_error_downloadはエラーCSVをzipでローカルに保存するだけで、中身はMCPの応答に含まれません。応答には保存先とバイト数、それに「File contents are not returned by MCP.」という注記が返ります。中身を読むには、エージェントが別の手段でファイルを開く必要があります。
また、このツールは既存ファイルを上書きしません。同じパスに2回保存しようとするとinvalid_output_pathで失敗するため、ファイル名に日時を入れています。
実行時は、zipを読むためのコマンドも許可します。
claude -p "/import-trace 54" \
--mcp-config mcp.json --strict-mcp-config \
--allowedTools "mcp__blastengine__blastengine_bulk_import_status,mcp__blastengine__blastengine_bulk_import_error_download,Bash(unzip -p:*),Skill" \
< /dev/null
3件中2件が失敗したジョブで実行した結果です。
## ジョブ54の調査結果
集計: 総数3 / 成功1 / 失敗2(status: FINISHED)
| 理由 | 件数 | 該当アドレス(先頭3文字以外は伏せ) |
|---|---|---|
| 無効なEメールアドレス形式 | 2 | not***********、bro************* |
元CSVの直し方
- email列の該当2行を修正します。
- 正しいアドレスが分からない場合は、その行を削除します。
再インポート時の注意
- 再インポートするCSVは、直した2行だけにしてください。
5ターン、約0.09ドルでした。
動かして分かったこと
エージェントが「差し込みコードが未置換」と誤判定する
/mail-traceの最初の版で、正常に届いている配信を調べさせたところ、調査メモに次の一文が紛れ込みました。
1267の本文には`__name__ 様`という差し込みプレースホルダが未置換のまま出ています
(配信詳細のtext_part)。実際にそのまま送られた可能性があります。
これは誤りです。delivery_getが返す本文は、差し込み前のテンプレートです。insert_codeを指定して送った配信でも、配信詳細の本文には__name__がそのまま残っています。MCP Inspectorから差し込みコード付きで送った別の配信(1259)でも、text_partは__prop1__ 様のまま返りました。配信詳細からは、差し込みが効いたかどうかは判断できません。
問い合わせ対応の最中にこの指摘が出ると、担当者は本来の調査と関係のない確認に時間を取られます。スキルの「注意」に1行足して、この点に触れないようにしました。エージェントに調査を任せるときは、ツールの返す値の意味を取り違えやすい箇所を、先回りして書いておく必要があります。
届いていないのに迷惑メールフォルダの確認を頼む
同じく最初の版では、DROP(相手に届いていない)ケースでも、顧客向け回答案に「迷惑メールフォルダをご確認ください」が入りました。定番の文言を機械的に並べた結果です。相手にとっては無駄な確認になるので、「注意」に書いて止めました。
ところが、DROPだけを名指しした注意では、ログが無い(そもそも送っていない)ケースで同じ文言が残りました。注意を「DROPやログなしのように相手に届いていないケース」と書き直したところ、どちらの回答案からも消えました。エージェントは書かれた条件を広げて解釈してはくれないので、除外したい場面は具体的に列挙します。
注意を足したあとの版で3つのケースを実行し直し、差し込みコードへの誤った指摘と、届いていないケースでの迷惑メールフォルダの案内が、どちらも出ないことを確認しています。
伏せ字の指示は完全には守られない
/import-traceでは「アドレスは先頭3文字以外を伏せる」と指示しました。表の中では守られましたが、別の実行では「直し方」の説明文で元のアドレスをそのまま書いていました。
個人情報を画面や報告に出したくない場合、プロンプトでの指示だけでは担保になりません。調査結果をチャットに貼る運用なら、貼る前に人が確認する手順を残しておくべきです。
パイプでつないだコマンドは許可ルールに一致しない
/import-traceの最初の版では、エージェントがunzip -p ... | sed ...のようにコマンドをつないで実行しようとしました。Bash(unzip -p:*)の許可ルールには一致しないため、実行は拒否されます。非対話モードでは確認のダイアログが出ないので、そのままpermission_denialsに記録されて終わりです。
スキルに「コマンドは単独で実行する」と書いたところ、拒否は0件になりました。許可リストを広げるより、スキル側で実行の仕方を指定するほうが安全です。
つまずきやすいポイント
| 症状 | 原因と対処 |
|---|---|
| 問い合わせのアドレスでログが見つからない |
emailは完全一致で、大文字・小文字の違いでも0件になる。実際に登録・送信されたアドレスの表記を確認する |
| 数か月前の不達が調べられない | 配信情報とログは配信開始から62日で削除される。問い合わせが遅れがちなら、日次で自分側にログを退避しておく |
| 日時の指定で弾かれる | オフセット付きISO 8601が必須。2026-08-26のような日付のみは不可 |
| 配信一覧ではSENTなのに届いていない | 配信単位のステータスは処理の完了を表すだけ。宛先単位のログかdrop_countなどの件数を見る |
| エラーCSVの保存に失敗する | 同名ファイルがあると上書きしない。ファイル名に日時を入れる |
| エラーCSVの中身が報告に出てこない | MCPは中身を返さない。unzip -pなどで読む許可が必要 |
| スキルが呼ばれない |
.claude/skills/<名前>/SKILL.mdの配置と、起動したディレクトリを確認する。非対話ではSkillの許可も必要 |
| 利用状況の件数が送信数と合わない | 使用量の集計には反映のラグがある。updated_timeで集計時刻を確認する |
まとめ
運用・保守の問い合わせ対応をblastengine MCPで定型化するポイントは3つです。
- 調査は参照系ツールだけで閉じる。送信系フラグを立てない設定を調査専用に用意しておけば、調査の途中で誤って送る心配がない
- 手順と判定基準をスキルに書き残す。「0件なら表記と期間を確かめてからアプリ側へ」「配信単位のSENTは全員に届いた意味ではない」といった切り分けを先に書いておくと、誰が呼んでも同じ深さで調べられる
- 試しに動かして、誤判定を「注意」として足していく。差し込み前のテンプレートを未置換と読み違えるような誤りは、実際のログを相手に動かさないと見つからない
調査のたびにプロンプトを書くのではなく、手順をスキルという形で育てていくと、担当者が替わっても対応の質が落ちにくくなります。
blastengine MCPサーバーへの要望や感想は、以下のアンケートフォームから送ると開発チームに届くみたいです。
https://docs.google.com/forms/d/e/1FAIpQLSdNZ9TswUT3JEv3pMHkJGqOvgmsXuKP1smkJiNEsqbTVDwRyg/viewform