Что нужно агенту и чего не даёт REST API
На этом сайте есть REST API, и программист, который его интегрирует, справится без проблем. С агентом иначе: он не может открыть справочник, выбрать нужный из трёх эндпоинтов и вручную собрать запрос с правильной строкой параметров. Вместо этого он спрашивает сервер, что тот умеет, получает машиночитаемые схемы и вызывает одну из них.
Поэтому всё, что умеет сервис, доступно и во втором виде — как инструменты. Их шесть, и между вызовами не нужно хранить никакого состояния:
| Инструмент | Для чего нужен |
|---|---|
create_inbox | Придумывает новый адрес, который агент может сразу отдать. На сервере при этом ничего не резервируется, поэтому вызов не может провалиться. Принимает необязательный читаемый prefix; уникальность обеспечивает случайный суффикс. |
list_domains | Публичные домены, которыми может пользоваться кто угодно, — пригодится, когда форма только что отклонила один из них. |
list_messages | Всё, что скопилось по адресу, от нового к старому. Отвечает сразу же, даже если там пусто. |
read_message | Одно письмо целиком: отправитель, тема, текст, HTML, вложения. Именно здесь находится код или ссылка для входа. |
wait_for_message | Не отвечает, пока что-нибудь не придёт, а затем возвращает письмо целиком. Инструмент, который нужно вызывать сразу после отправки формы. |
delete_message | Удаляет письмо сразу, а не ждёт 5 дней до истечения срока. Идемпотентен, поэтому повторный вызов агенту ничего не стоит. |
На initialize сервер также отвечает коротким абзацем инструкций, который большинство клиентов сразу передают модели. Поэтому агент с самого начала знает, для чего этот сервис нужен и в чём его единственная реальная оговорка, — и никому не приходится прописывать это в промпте.
Подключить клиент одной строкой
Эндпоинт — это один URL, и на публичных доменах регистрироваться незачем. Любой MCP-клиент принимает конфигурацию одной и той же формы — Claude Desktop, Claude Code, Cursor, Continue, OpenAI Agents SDK и всё остальное, что говорит на этом протоколе:
{"mcpServers":{"grabmail":{"url":"https://grabmail.io/mcp"}}}Транспорт — Streamable HTTP: один POST с JSON-RPC 2.0, один JSON-ответ, никакого удерживаемого потока. Поэтому всё можно проверить из терминала ещё до того, как в дело вступит агент:
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 — там указаны тот же эндпоинт и его транспорт.
Весь цикл регистрации — в четырёх вызовах инструментов
Вот последовательность, которая нужна почти любому агенту, и больше в ней ничего нет:
- Вызовите
create_inbox. В ответ придут адрес, алиас, домен и пометка, какой из двух отдавать. Ничего при этом не создаётся — ящик начинает существовать, когда в него попадает первое письмо. - Впишите алиас в форму. Сервис, в котором идёт регистрация, получает рабочий адрес, который доставляет письма в ящик, но не даёт их прочитать.
- Вызовите
wait_for_messageс этим адресом. Сразу после отправки формы, а не по таймеру. Вызов не отвечает, пока не придёт письмо, — агенту не нужно писать цикл опроса самому. - Возьмите код из письма. Вместе с ответом на ожидание приходит и всё тело письма, так что второй вызов обычно вообще не нужен —
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 ..."
}- Зачем вообще нужен предел
- Каждая секунда ожидания — это серверный обработчик, который всё это время бездействует, а число обработчиков ограничено. Ожидание длиной в пять минут означало бы, что один агент занимает слот, который нужен ещё сотне других. 25 секунд укладываются и в тайм-аут по умолчанию у любого клиента, поэтому вызов успевает вернуть ответ раньше, чем клиент сам от него откажется.
- Одновременно — не больше 8 ожиданий
- Сверх этого числа инструмент отвечает мгновенно —
timed_outи пометка о том, что произошло. Услышать «попробуй ещё раз» лучше, чем молча встать в очередь за семью другими агентами, не зная об этом. - Фильтры, чтобы не сработать на чужое письмо
from_containsиsubject_containsзаставляют ожидание игнорировать всё остальное, что придёт за это время.since_idнужен, когда в ящике уже что-то было: передайте в нём id последнего уже увиденного письма, и вызов завершится только на действительно новом.
Раздавайте алиас, опрашивайте адрес
У каждого ящика здесь есть второй адрес длиной в двенадцать символов: он доставляет письма в тот же ящик, но не позволяет их прочитать. Для агента это различие важнее, чем для человека, — агент без колебаний вставит то, что ему дали, в любое подвернувшееся поле.
Поэтому create_inbox не просто возвращает адрес наудачу. Он возвращает оба значения и next_step с пояснением, где что, — агент читает собственный результат вызова, и инструкция оказывается именно там, где нужна, а не на странице документации, которую в этой цепочке никто не читает.
Что написать в инструкциях самого агента
Инструменты достаточно хорошо описывают себя сами, и способная модель справляется с этим и без подсказок. Но пять строк превращают вероятность в надёжность:
- Один адрес на одну регистрацию. Не один и тот же адрес везде: ящик, где лежит почта от шести сервисов, — это шесть подтверждений, которые агенту нужно различать, и одна утечка, которая раскрывает их все разом.
- Раздавайте алиас и никогда — адрес. Стоит сказать это прямо, даже если то же самое написано и в результате вызова.
- Вызывайте
wait_for_messageсразу после отправки формы и вызывайте его снова приtimed_out, не считая это сбоем. Два-три раза — это нормально. - Передавайте
since_id, если ящик не новый, — иначе ожидание завершится на старом письме, и агент прочитает код, который истёк час назад. - Удаляйте письмо, как только код использован. Это не обязательно — всё и так исчезнет через 5 дней, — но так окно закрывается раньше, а стоит это один идемпотентный вызов.
В виде блока инструкций это выглядит примерно так:
Когда нужен email-адрес, вызывайте create_inbox и раздавайте АЛИАС —
то, что он возвращает, а не адрес. Сразу после отправки формы вызывайте
wait_for_message с этим адресом. Если ответ — timed_out, вызывайте снова:
это нормально, ничего не потеряно. Если в ящике уже что-то было, передайте
since_id. Удаляйте письмо, как только код использован.Ограничения, которые стоит знать, прежде чем строить на этом
Все они опубликованы, а не найдены методом проб, и ни для одного нет тарифа, который бы его снимал:
| Ограничение | Значение | Что это значит для агента |
|---|---|---|
| Одно ожидание | 25 секунд | Затем timed_out. Вызовите ещё раз — это не ошибка. |
| Ожиданий одновременно | 8 | Сверх этого инструмент отвечает сразу и сообщает об этом. В таком случае используйте list_messages. |
| Чтений | Одно в секунду на адрес | Это намного больше, чем нужно циклу вызовов. Блокирующее ожидание — один запрос, а не шестьдесят. |
| Размер письма | 5 MB | Отклоняется прямо во время SMTP-сессии, так что об этом узнаёт отправитель, а не агент, ожидающий того, что никогда не придёт. |
| Срок хранения | 5 дней | Жёсткий предел, который обеспечивает фоновая задача. Всё, что агенту нужно сохранить, ему придётся записать самому. |
Эндпоинта для отправки нет, как и инструмента для неё. Сервис только принимает почту, и именно это не даёт неавторизованному ящику превратиться в спам-релей, — так что агенту, которому нужно ответить человеку, понадобится настоящий почтовый ящик где-то ещё.
Когда форма отклоняет публичные домены
Многие сервисы ведут списки доменов одноразовой почты, и три публичных домена отсюда в них тоже есть. Для агента это выглядит как форма, которая отклоняет только что полученный адрес, или, что хуже, принимает его и ничего не отправляет.
Надёжный ответ — домен, которым владеете вы сами. Одна MX-запись превращает на нём любой адрес в ящик здесь же, его нет ни в одном списке, потому что на этом сайте он нигде не упомянут, и те же шесть инструментов работают на нём без изменений — кроме 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, он же используется по умолчанию в текущих клиентах, а поддержка трёх версий протокола позволяет подключиться и более старому клиенту.
Почему wait_for_message возвращает timed_out?
Потому что одно ожидание намеренно ограничено 25 секундами. Это не ошибка, и ничего не потеряно — вызовите его снова. Письмо нередко идёт дольше, чем можно подумать по странице, которая его обещала, и два-три ожидания подряд — обычное дело при регистрации.
Может ли агент использовать вместо этого мой собственный домен?
Да, и меняется только адрес. Направьте MX-запись на smtp.grabmail.io — и любой адрес на этом домене станет доступен через те же инструменты. Только create_inbox работает исключительно с публичными доменами, потому что именно он придумывает имя за вас.
Может ли агент отправлять почту через этот сервис?
Нет. Инструмента и эндпоинта для отправки нет — так и задумано: неавторизованный сервис, который умел бы отправлять почту, превратился бы в спам-релей за день. Наш SPF — v=spf1 -all, а DMARC — p=reject, так что всё, что якобы приходит с адреса отсюда, — подделка.
Ящик приватный?
Нет, и это единственная оговорка, которую агенту стоит сообщить явно. На публичном домене прочитать ящик может любой, кто знает или угадал адрес. Используйте адрес, который невозможно угадать, раздавайте алиас, а не адрес, и никогда не подпускайте к этому ящику ничего личного.
Могут ли два агента одновременно ждать на одном адресе?
Могут, и письмо при получении достанется обоим. Ограничено число ожиданий одновременно по всему сервису — 8; сверх этого инструмент сразу отвечает и сообщает об этом, а list_messages при этом продолжает работать.
Сколько хранятся письма?
5 дней с момента доставки, прочитано оно или нет, и продлить это никакой настройкой нельзя. У каждого письма есть поле expires_at, так что агенту не нужно вычислять эту дату самому.
Чем это отличается от вызова REST API из скрипта?
Для скрипта — ничем, и REST API там подходит лучше: руководство о сквозном тестировании проверки email разбирает именно эту форму, вместе со сроками и вспомогательными функциями. MCP нужен там, где цикл никто не писал: модель сама решает открыть ящик, и ей нужно, чтобы инструменты были обнаруживаемыми, а не задокументированными.


