REST APIでは満たせない、エージェントに必要なもの
このサイトにはREST APIがあり、それを組み込むプログラマーであれば何の問題もありません。しかしエージェントはそうはいきません。リファレンスを開いて3つのエンドポイントのどれが必要かを判断し、正しいクエリ文字列を持つリクエストを手作業で組み立てる、ということができないのです。エージェントはサーバーに何ができるかを尋ね、機械可読なスキーマを受け取り、その一つを呼び出します。
そのため、このサービスができることはすべて、ツールとしてもう一度公開されています。全部で6つあり、呼び出しの間で管理すべき状態もありません。
| ツール | 用途 |
|---|---|
create_inbox | エージェントがすぐに渡せる新しいアドレスを生成します。サーバー側では何も予約されないため、失敗することがありません。読みやすいprefixを任意で指定でき、一意性はランダムな接尾辞で保たれます。 |
list_domains | 誰でも使える公開ドメインの一覧です。フォームがそのうちの一つを拒否したときに役立ちます。 |
list_messages | あるアドレスに届いているものすべてを、新しい順に返します。何もない場合でも、即座に返ってきます。 |
read_message | 1通のメッセージを、送信者・件名・プレーンテキスト・HTML・添付ファイルまで含めて丸ごと返します。コードやサインインリンクはここにあります。 |
wait_for_message | 何か届くまで待機し、届いたら丸ごと返します。フォームを送信した瞬間に呼ぶべきツールです。 |
delete_message | 5日の期限切れを待たずに、その場でメッセージを削除します。何度呼び出しても安全なので、エージェントが再試行しても問題はありません。 |
サーバーはinitializeに対しても、短い説明文を返します。ほとんどのクライアントはこれをそのままモデルに渡します。そのため、誰かがプロンプトに書き込むまでもなく、エージェントはこのサービスの目的とただ一つの注意点をあらかじめ知った状態で動き出せます。
クライアントを1行で接続する
エンドポイントはURL一つだけで、公開ドメインを使う分には登録も何も必要ありません。Claude Desktop、Claude Code、Cursor、Continue、OpenAI Agents SDK、その他このプロトコルを話せるものなら、どのMCPクライアントでも設定の形は同じです。
{"mcpServers":{"grabmail":{"url":"https://grabmail.io/mcp"}}}トランスポートはStreamable HTTPです。JSON-RPC 2.0を運ぶPOSTが1回、返ってくるJSONが1回、開いたままのストリームはありません。そのため、エージェントを一切介さずに、ターミナルだけで全体を確認できます。
curl -sX POST https://grabmail.io/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'人に尋ねる前にサーバーを自動的に探しにいくクライアントは、そのドメインの/.well-known/mcp.jsonを見つけます。そこには同じエンドポイントとそのトランスポートが記載されています。
サインアップの一連の流れを、4回のツール呼び出しで
ほとんどのエージェントに必要なのはこの手順だけで、それ以上のものはありません。
create_inboxを呼ぶ。アドレス、エイリアス、ドメイン、そしてどちらを渡すべきかを伝える注記が返ってきます。この時点では何も作成されていません。メールボックスは、最初のメッセージが届いた瞬間に存在し始めます。- フォームにエイリアスを入力する。サインアップ先のサービスは、メールボックスに届くけれども中を読むことはできない、有効なアドレスを手に入れます。
- そのアドレスで
wait_for_messageを呼ぶ。タイマーを仕込むのではなく、送信した直後に呼びます。これはブロッキング呼び出しであり、エージェント自身がループを書いてポーリングする必要はありません。 - メッセージからコードを読み取る。待機の応答に本文全体が含まれているため、通常は2回目の呼び出しは不要です。
read_messageが必要になるのは、以前に届いたものを読み返すときだけです。
wait_for_messageがメールより先に返ってくる理由
エージェントを実用的にしているのはこのツールであり、同時に挙動が意外に思われがちなツールでもあるので、少し時間をかけて説明します。呼び出しはこのような形です。
curl -sX POST https://grabmail.io/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":
{"name":"wait_for_message",
"arguments":{"address":"demo.5kuqarzuch@grabmail.io",
"subject_contains":"code"}}}'最大25秒間ブロックします。その間に何も届かなくても失敗にはなりません。ありのままの応答を返し、もう一度呼び出すよう求めてきます。
{
"timed_out": true,
"waited_seconds": 25,
"message": null,
"note": "Nothing arrived yet. Call wait_for_message again ..."
}- なぜ上限を設けているのか
- 待機している1秒ごとに、サーバー側のワーカーは眠っているだけの状態になり、しかもその数には上限があります。5分も続きうる待機を許せば、1つのエージェントが、他の100件が必要としている枠を占有し続けることになります。25秒という長さは、どのクライアントのデフォルトタイムアウトにも収まる範囲でもあるため、クライアント側が諦めるより前に呼び出しが返ります。
- 同時に待機できるのは8件まで
- それを超えると、ツールは即座に
timed_outとその旨の注記を返します。知らないまま他の7件のエージェントの後ろに並ばされるより、出直すよう伝えられるほうがましです。 - フィルタリングで、無関係なメールに待機を終わらせない
from_containsとsubject_containsを指定すると、その間に届く他のメールを待機は無視するようになります。メールボックスに既に何か入っている場合はsince_idを渡してください。既に見た最新のidを渡せば、本当に新しいメールだけが呼び出しを満たします。
渡すのはエイリアス、確認するのはアドレス
ここにあるすべてのメールボックスには、12文字からなる2つ目のアドレスがあります。同じメールボックスに配信はできますが、そこから読み出すことはできません。この違いは、人よりもエージェントにとってはるかに重要です。エージェントは、渡されたものを見つけたフィールドに深く考えずに貼り付けてしまうからです。
そのためcreate_inboxは、アドレスを一つ返して終わり、という作りにはなっていません。両方を返し、どちらがどちらかを示すnext_stepも一緒に返します。エージェントは自分自身のツール結果を読むので、その指示は、ループの中の誰も読めないドキュメントページに置かれるのではなく、必要な場所に届きます。
エージェント自身の指示文に何を書くか
ツール自体の説明が十分に整っているため、能力の高いモデルなら指示がなくても正しく振る舞います。次の5行を加えれば、「たいてい正しい」が「確実に正しい」に変わります。
- サインアップ1件につきアドレス1つ。あらゆる場所で同じアドレスを使い回さないでください。1つのメールボックスに6つのサービス分のメールが集まれば、エージェントは6件の確認メールを区別しなければならなくなり、しかも1回の漏えいで全部が露出します。
- 渡すのは常にエイリアスで、アドレスは決して渡さない。ツールの返り値にも書かれてはいますが、明示的に伝えておく価値があります。
- 送信した直後に
wait_for_messageを呼ぶこと。timed_outが返っても失敗とはみなさず、もう一度呼びます。2〜3回は普通のことです。 - メールボックスが新規でない場合は
since_idを渡すこと。渡さないと、古いメッセージが待機の条件を満たしてしまい、エージェントが1時間前に期限切れになったコードを読んでしまいます。 - コードを使い終えたら、メッセージを削除する。必須ではありません。どのみち5日ですべて消えるからです。ただし、これで窓口を早く閉じられますし、呼び出しが1回増えるだけで、副作用もありません。
指示文としてまとめると、だいたいこのくらいの長さになります。
メールアドレスが必要になったら、create_inboxを呼び、返ってきたエイリアスを渡してください。
アドレスそのものは渡さないでください。フォームを送信した直後に、そのアドレスで
wait_for_messageを呼んでください。timed_outが返ってきても、それは正常な動作です。
何も失われていないので、もう一度呼んでください。メールボックスに既に何か入っていた場合は
since_idを渡してください。コードを使い終えたら、メッセージを削除してください。作り込む前に知っておくべき上限
どれも隠されたものではなく公開されており、上位プランで緩和されるものもありません。
| 上限 | 値 | エージェントにとっての意味 |
|---|---|---|
| 1回の待機 | 25秒 | その後はtimed_out。もう一度呼び出してください。エラーとして扱わないでください。 |
| 同時待機数 | 8 | それを超えると、ツールは即座にその旨を返します。list_messagesに切り替えてください。 |
| 読み取り | アドレスごとに1秒に1回 | ツール呼び出しのループが必要とする頻度をはるかに上回ります。ブロッキング待機は1回のリクエストであり、60回ではありません。 |
| メッセージサイズ | 5 MB | SMTPのやり取りの中で拒否されるため、エージェントが決して来ないものを待ち続けるのではなく、送信元にその場で伝わります。 |
| 保持期間 | 5日間 | ジョブによって強制される絶対的な上限です。エージェントが保持し続けたいものは、自分自身で書き留めておく必要があります。 |
送信用のエンドポイントもツールもありません。このサービスは受信専用であり、そのおかげで認証なしのメールボックスがスパムリレーになることを防いでいます。そのため、人に返信する必要があるエージェントには、どこか別の場所に本物のメールボックスが必要です。
フォームが公開ドメインを拒否するとき
多くのサービスは使い捨てメールドメインのリストを保持しており、ここにある3つの公開ドメインもそこに含まれています。エージェントから見ると、これは渡されたばかりのアドレスをフォームが拒否する、あるいはもっと悪いことに、受け付けたのに何も送られてこない、という形で現れます。
根本的な答えは、自分が所有するドメインを使うことです。MXレコードを1件追加するだけで、そのドメイン上のすべてのアドレスがここのメールボックスになります。このサイトのどこにも表示されないため誰のリストにも載らず、しかも同じ6つのツールがそのまま使えます。唯一の例外はcreate_inboxで、これは公開ドメイン上でアドレスを生成するためのツールだからです。エージェントは単にyou-pick-it@your-domainを使い、そのアドレスでwait_for_messageを呼ぶだけです。
あなたのドメイン用のMXレコード10 smtp.grabmail.io
詳しい手順はこちら。レコードの内容、それを公開することで何が証明されるか、そしてパスワードのないメールボックスの正直な限界について解説しています。
エージェントにさせてはいけないこと
ここは正直に言っておくべき部分であり、半日を無駄にしないための部分でもあります。
- 復元が必要になるようなものには使わないこと。お金、本人確認、仕事に関わるものはすべて対象です。メールボックスは5日後には空になり、アドレスを知っている人なら誰でも読めます。来年そこに送られたパスワード再設定メールは、誰にも届かないか、あるいは自分以外の誰かに届きます。
- 第二要素として使わないこと。パスワードのないメールボックスは、要素にはなり得ません。
- プライベートな用途には使わないこと。私たちがそれを読んでいるからではなく、そこで唯一の秘密であるはずのアドレスを、エージェントがログや会話履歴、コミットメッセージにそのまま書き残してしまうことが十分にあり得るからです。
- 大量利用には使わないこと。エージェントが何百ものアカウントを開く行為は、まさにあらゆるブロックリストが存在する理由そのものであり、公開ドメインが他の全員のために拒否されるようになる、最も早い道でもあります。
本来の用途どおりに使えば——エージェントと、本当に頼まれた作業との間に立ちはだかる確認ステップとして——これは、エージェントを確実に止めてしまうその一段階を取り除いてくれます。
質問
APIキーやアカウントは必要ですか?
いいえ。公開ドメイン、ツール、そして独自ドメインのいずれも、無料かつ認証不要です。キーが必要になるのは、リクエストにより非公開にされたドメインだけで、その場合はAuthorizationヘッダーが必要になります。
どのクライアントで使えますか?
Model Context Protocolを話せるクライアントであれば、どれでも使えます。Claude Desktop、Claude Code、Cursor、Continue、OpenAI Agents SDKなどです。トランスポートはStreamable HTTPで、現行のクライアントの多くが標準で使う方式です。プロトコルのバージョンは3つ受け付けているため、古いクライアントでも接続できます。
なぜwait_for_messageはtimed_outを返してくることがあるのですか?
1回の待機は意図的に最大25秒に制限されているためです。エラーではなく、何も失われてもいません。もう一度呼び出してください。メールは、送信元のページが示唆するよりも時間がかかることが日常的にあり、2〜3回連続で待機するのは普通のサインアップです。
自分のドメインを代わりに使うことはできますか?
できます。変わるのはアドレスだけです。MXレコードをsmtp.grabmail.ioに向ければ、そのドメイン上のすべてのアドレスが同じツールで読めるようになります。create_inboxだけは公開ドメイン専用です。これはアドレスを自動生成するツールだからです。
エージェントはこれを使ってメールを送信できますか?
できません。送信用のツールもエンドポイントも、意図的に用意していません。認証なしでメールを送れるサービスは、1日でスパムリレーになってしまいます。SPFはv=spf1 -all、DMARCはp=rejectに設定しているため、ここのアドレスから来たと称するメールはすべて偽装です。
受信箱は非公開ですか?
いいえ。これはエージェントに明示的に伝えておくべき、唯一の注意点です。公開ドメインでは、アドレスを知っている、あるいは推測できる人なら誰でも読めます。推測されにくいアドレスを使い、渡すのはアドレスではなくエイリアスにし、プライベートなものは一切近づけないでください。
2つのエージェントが同じアドレスで同時に待機できますか?
できます。メッセージが届けば、両方に渡されます。制限されているのは、サービス全体で同時に発生している待機の数のほうで、上限は8件です。それを超えると、ツールは即座にその旨を返しますが、list_messagesは引き続き使えます。
メッセージはどのくらいの期間残りますか?
既読・未読にかかわらず、到着から5日間です。これを延長する設定はありません。すべてのメッセージにはexpires_atが付与されているため、エージェントがその日付を自分で計算する必要はありません。
スクリプトからREST APIを呼ぶ場合と、何が違いますか?
スクリプトにとっては違いはなく、REST APIのほうが向いています——確認フローをテストするガイドで、その形、期限の扱い、ヘルパーまで含めて解説しています。MCPが向いているのは、誰もループを書いていない場合です。モデル自身が受信箱を開くかどうかを判断し、ツールはドキュメントを読むのではなく発見できる必要があります。


