Skip to main content
Glama

mailwarden

npm license Node Website Smithery Available on CodeGuilds

Надёжный, нативный сервер Gmail MCP — полная триаж почтового ящика для ИИ-ассистентов, с функцией, которой нет ни у одного другого MCP-сервера для Gmail: отложенная отправка на уровне почтового ящика.

Основные возможности

  • Отложенная отправка — единственная на уровне почтового ящика в MCP-сервере для Gmail. Архивируйте цепочку сейчас, а она снова появится во входящих в указанную дату. Основана на датированных метках + очистке, поэтому работает из любого клиента, видна в самом Gmail и переживает перезапуски. (Там, где другой сервер предлагает «отложенную отправку», это локальный список напоминаний — письмо никогда не покидает входящие и не возвращается в них.)

  • Поиск, которому можно доверять. threads.list Gmail — вызов, через который проходит любой поиск по цепочкам — может отвечать на is:unread на основе устаревшего состояния прочтения на уровне цепочки: в одном реальном почтовом ящике 86% возвращённых цепочек не содержали ни одного непрочитанного сообщения; во втором почтовом ящике отклонений не было вовсе. Вы не можете определить, в каком почтовом ящике находитесь, не заглянув внутрь, поэтому search повторно проверяет каждый результат на соответствие его актуальным меткам. Пагинация через pageToken/nextPageToken.

  • Массовые операции, которые масштабируются. bulk_modify архивирует/маркирует всё, соответствующее запросу, со скоростью 1000 сообщений на один API-запрос — с отчётом о частичном успехе для каждого блока вместо принципа «всё или ничего». Очистка отложенной отправки использует тот же пакетный путь.

  • Структурированные выходные данные. Каждый инструмент объявляет outputSchema и возвращает проверенные structuredContent вместе с экранированным JSON-текстом — никаких догадок при разборе для клиентов.

  • Небольшая поверхность для атак. Нет инструментов отправки (нет пути эксфильтрации для писем, внедрённых через промпт), опциональный режим «только чтение», нет телеметрии, нет открытых портов по умолчанию, защита загрузок от символических ссылок, экранированный вывод. Одно осознанное исключение: unsubscribe / bulk_unsubscribe (тариф manage) обращаются к конечной точке отказа от подписки, указанной в заголовке самого сообщения — единственный хост, не принадлежащий Google, которого когда-либо достигает mailwarden, а развёртывание уровня read вообще не выполняет исходящих запросов. Подробности в разделах Безопасность и конфиденциальность и Отписка.

  • Корректная работа с реальной почтой. Заголовки RFC 2047 декодируются (=?UTF-8?B?…?= → читаемый текст), тела декодируются в их заявленной кодировке (без кракозябр для писем в ISO-8859-1/Shift_JIS), 429/5xx повторяются с экспоненциальной задержкой.

Related MCP server: Gmail MCP

Зачем

Коннекторы, которые синхронизируют или кешируют ваш почтовый ящик, могут отставать от него — и даже собственный поисковый индекс Gmail иногда неточен (см. ниже). mailwarden общается напрямую с живым Gmail API (без кешированного снимка) и перепроверяет то, что возвращает индекс, так что вы видите именно то, что есть на самом деле. Это универсальный слой возможностей Gmail — храните свои собственные правила/логику в вашем ИИ-клиенте, а не на сервере.

search идёт на шаг дальше, чем сырой API: индекс threads.list Gmail может отвечать на операторы состояния прочтения на основе устаревшей копии этого состояния, поэтому is:unread возвращает цепочки, которые вы дочитали недели назад — в одном измеренном почтовом ящике подавляющее большинство того, что было возвращено. Поскольку каждый результат всё равно извлекается в реальном времени, search перепроверяет однозначные предикаты (is:unread/is:read/is:starred/in:inbox/category:…, с отрицанием) по реальным меткам каждой цепочки и отбрасывает ложные срабатывания индекса.

По сравнению с другими MCP-серверами Gmail

Большинство MCP-серверов Gmail покрывают одну и ту же поверхность чтения/маркировки/отправки. Две возможности всё ещё уникальны для mailwarden (отложенная отправка на уровне почтового ящика, перепроверка поиска), а одно осознанное упущение является функцией безопасности, а не пробелом. Собственный сервер Google также уже, чем кажется: только черновики, и нет корзины, фильтров или отписки.

Возможность

mailwarden

Официальный Google

taylorwilsdon

a-bonus

klodr

Отложенная отправка на уровне почтового ящика — архивировать сейчас, вернуть во входящие в дату/время или по预设

Перепроверка результатов поиска — отбрасывает ложные срабатывания индекса цепочек по реальным меткам

Очистка / массовая операция по запросу — одно действие для каждой цепочки, возвращённой поиском

✅ 1000/запрос, частичный успех

⚠️ пакетная по явным id

⚠️ пакетная по явным id

Отписка — обзор по отправителям + одношаговая отписка RFC 8058, без необходимости в области отправки

⚠️ показан заголовок, без действия

Обзор триажа входящих — один вызов, который группирует то, что ожидает

✅ отправитель/метка/возраст + сигналы заголовков

✅ флаги эвристик + статистика

Серверные фильтры — правила, которые продолжают триаж без ассистента в цикле

✅ без пересылки

Нет инструментов отправки — по замыслу — внедрённое через промпт письмо не имеет пути эксфильтрации

✅ нет составления вообще

⚠️ только черновики

❌ отправляет

❌ отправляет

❌ отправляет

Уровни инструментов с минимальными привилегиями — области OAuth, выводимые из включённых вами инструментов

⚠️ разделение областей

⚠️ обратное: инструменты, ограниченные предоставленными областями

Шифрование токенов в покое (опционально)

✅ AES-256-GCM

н/д (хостинг)

Нет облака вендора — вы управляете сервером

❌ хостинг Google

Структурированные выходные данные — каждый инструмент объявляет outputSchema

Снимок на 16 августа 2026 года из публичной документации и исходных кодов каждого проекта; = не предлагается / не документировано. Столбцы — это серверы, к которым читатель, скорее всего, обратится: собственный сервер Google, два крупнейших сервера сообщества и klodr, который ближе всего подходит к собственному дизайну с минимальными привилегиями mailwarden. Возможность отправки указана как свойство безопасности: её отсутствие в mailwarden является намеренным (см. Безопасность и конфиденциальность). Последняя строка спрашивает, кто управляет сервером, а не где он работает: самостоятельный хостинг здесь является общей основой, и каждый сервер сообщества в этой таблице предлагает какое-либо удалённое развёртывание, кроме klodr (только stdio) — mailwarden через --http, taylorwilsdon через потоковый HTTP с OAuth 2.1, a-bonus на Cloud Run. Запуск одного из них на вашем собственном хосте — это не облачная копия; запуск на хосте вендора — это она.

Ров не в какой-либо отдельной строке — это отложенная отправка + проверка в реальном времени вместе: фактический слой рабочего процесса с входящими, который действует на текущее состояние почтового ящика, а не на кешированный снимок. Там, где другие догнали, это честно отмечено выше: шифрование в покое (taylorwilsdon), ограничение инструментов по областям (klodr), более богатая эвристика триажа для каждого сообщения (a-bonus) и массовая организация в почтовом ящике (хостинг mcpemails.com, у которого тоже нет отложенной отправки). Никто из них не действует на основе запроса и не проверяет ответ почтового ящика перед тем, как действовать на его основе.

Почему перепроверка важна — конкретный пример

Попросите ассистента «архивировать непрочитанные рекламные письма, которые уже обошли мои входящие», и он потянется к очевидному запросу category:updates is:unread -in:inbox. Сервер, который доверяет индексу Gmail, теперь архивирует цепочки, которые вы уже прочитали — письма, к которым вы не собирались прикасаться, исчезнувшие в результате массового действия, которое вы не можете легко отменить.

Измерено, а не утверждено. Один реальный почтовый ящик (~70 000 сообщений), 15.08.2026, только чтение:

Запрос (threads.list)

Возвращено нитей

С непрочитанным сообщением

Устаревшие

category:updates is:unread

131

17

87%

category:updates is:unread -in:inbox

128

14

89%

is:unread -in:inbox

235

99

58%

Индекс не игнорирует предикат — тот же запрос без is:unread возвращает более 800 нитей, значит, он применяется. Он применяется к состоянию прочитанности на уровне нити, которое не успело обновиться: нити, в которых каждое сообщение прочитано, всё ещё считаются непрочитанными. Одна возвращённая нить имела единственную метку SENT. И это не особенность экзотических комбинаций операторов: самый простой из трёх запросов тоже это показывает — с наименьшей долей (58%), но с наибольшим абсолютным числом ошибочных нитей (136).

Это именно индекс нитей. Тот же запрос, тот же почтовый ящик, та же минута, выполненный через messages.list: 19 сообщений, ни одного устаревшего. Так что это не «поиск Gmail ненадёжен» — это представление состояния прочитанности на уровне нитей отстаёт, в то время как представление на уровне сообщений — нет. search проходит через threads.list, именно поэтому он выполняет повторную проверку.

Второй почтовый ящик, измеренный тем же способом в тот же день, не дрейфовал вообще — нулевое количество попаданий в сыром индексе для is:unread, хотя он много раз в день помечается как прочитанный через API. Так что это свойство конкретного почтового ящика, а не Gmail повсеместно. Что их различает, остаётся открытым: они различаются по объёму (примерно на три порядка) и возрасту, и во втором отсутствует нечто более фундаментальное — ни одна нить в нём никогда не была архивирована, оставаясь непрочитанной, а это единственная форма, в которой может проявиться устаревшее состояние прочитанности. Так что это не контрпример к какой-либо конкретной причине; это почтовый ящик без кандидата.

В этом и суть: сервер не может знать, с каким типом почтового ящика он имеет дело. Повторная проверка ничего не стоит там, где ничего не дрейфует, и спасает там, где дрейфует — в приведённых измерениях каждая нить, отброшенная search, была действительно прочитана, и он не отбросил ни одного действительно непрочитанного письма.

Где это не бесплатно: массовые инструменты. search выполняет повторную проверку, потому что всё равно загружает каждое совпадение; bulk_modify (и проход create_filter's applyToExisting) рассчитан на тысячи сообщений, где одна загрузка на каждое совпадение — это другой порядок затрат. Они действуют на основе того, что возвращает индекс, — поэтому теперь они сообщают unverifiedPredicates, условия из вашего запроса, которые были приняты на слово индексу (+UNREAD, -INBOX, …). Пустое значение означает, что нечему было не доверять. Непустое, а результат должен быть точным по состоянию прочитанности? Сначала разрешите набор с помощью search и действуйте на основе этих идентификаторов нитей. dryRun не устраняет этот разрыв: он перечитывает тот же индекс, так что подтверждает размер набора, но никогда — его правильность.

mailwarden всё равно загружает каждое совпадение в реальном времени, поэтому search перепроверяет однозначные предикаты (is:unread, is:read, in:inbox, category:…, с отрицанием) по истинным меткам каждой нити и отбрасывает ложные срабатывания индекса до того, как их увидит какой-либо инструмент. Затем массовое действие выполняется именно над тем набором, который вы запросили. В этом разница между действием на основе того, что Gmail проиндексировал, и действием на основе того, что действительно находится в почтовом ящике прямо сейчас — и именно поэтому откладывание/очистку можно безопасно доверить ассистенту: очистка извлекает только те нити, чьё откладывание действительно истекло, проверенное по живым меткам во время выполнения.

Убедитесь сами — учётная запись Gmail не нужна. Из клона репозитория (демонстрация — это скрипт проверки, находящийся только в репозитории, не часть npm-пакета):

git clone https://github.com/csitte/mailwarden && cd mailwarden
npm install && npm run build
node scripts/demo-reverify.mjs

Рядом с ним есть второй скрипт, node scripts/probe-reverify.mjs, который измеряет то же самое в вашем почтовом ящике вместо фиктивного — только чтение, только метаданные (тема, отправитель и тело не загружаются), выводя количество и названия меток. Именно так были получены числа выше, и так вы можете проверить, дрейфует ли ваш почтовый ящик вообще.

Демонстрация запускает реальный search() против фиктивного Gmail API, чей индекс намеренно устарел (возвращает прочитанную нить для запроса is:unread, в точности как Gmail) и показывает, как mailwarden отбрасывает ложное срабатывание. Она проверяет результат, поэтому завершается с ненулевым кодом, если поведение когда-либо регрессирует. Тот же случай закреплён модульными тестами в test/gmail.test.ts ("drops index false positives via live-label re-verify").

Инструменты

Инструмент

Что делает

search

Синтаксис запросов Gmail → сводки по темам (от/тема/дата/метки/фрагмент); предикаты состояния чтения/категории повторно сверяются с живыми метками каждого результата; постраничная навигация через pageToken/nextPageToken. Каждый результат содержит signalsnewsletter (List-Id / List-Unsubscribe / Precedence bulk или list), automated (Auto-Submitted, auto-reply/suppress headers, отправители вида no-reply), calendar (часть text/calendar или .ics), replyToMismatch (Reply-To на другом домене, отличном от From; поддомен того же домена считается тем же) — читается из заголовков/MIME первого сообщения, без дополнительного вызова

get_thread

Полная цепочка: заголовки, тела в plaintext + HTML, метаданные вложений

list_labels

Все метки (системные и пользовательские)

get_profile

Адрес подключенного аккаунта + общее количество сообщений/цепочек — подтвердите, какой почтовый ящик подключен, прежде чем действовать

triage_digest

Структурированный обзор среза почтового ящика для принятия решений: топ-отправители (каждый с сигналами, которые несут его цепочки), категории по меткам и возрасту, количество непрочитанных и вложений, а также сколько цепочек являются рассылками / автоматическими / приглашениями в календарь / несовпадениями reply-to — вместо сырого списка цепочек

list_unsubscribe

Какие возможности отказа от подписки рекламирует цепочка (List-Unsubscribe) — ни с кем не связывается

list_subscriptions

Срез почтового ящика, сгруппированный по отправителю: количество цепочек/непрочитанных, временной промежуток, в течение которого каждый наблюдался, и возможности отказа от подписки каждого — один запрос заголовка на отправителя, ни с кем не связывается. sendersFound сообщает, сколько было отправителей до того, как topN обрезал список

create_label

Создать пользовательскую метку (идемпотентно; вложенность через Parent/Child) и вернуть её id

modify_labels

Добавить/удалить метки по имени или id — неизвестное имя в add создаётся автоматически (archive = удалить INBOX, read = удалить UNREAD)

bulk_modify

Пакетное изменение меток для каждого сообщения, соответствующего запросу — 1000 сообщений на один API-запрос, частичный успех сообщается по частям (список thread-id ограничен 500, modifiedThreadCount содержит общее количество). Действует на сыром индексе, поэтому unverifiedPredicates перечисляет условия, которые не удалось подтвердить (см. ниже). dryRun: true выполняет запрос и сообщает о найденных цепочках и метках, которые были бы созданы, ничего не затрагивая

archive / mark_read / mark_unread

Удобные обёртки

trash / untrash

Переместить в корзину / восстановить из корзины

download_attachment

Сохранить вложение в локальный путь (никогда не перезаписывает — при коллизиях добавляется числовой суффикс)

unsubscribe

Отказ от подписки в один клик (RFC 8058) с использованием конечной точки из собственного заголовка сообщения — единственный инструмент, который связывается с не-Google хостом (подробнее)

bulk_unsubscribe

То же самое для нескольких цепочек, последовательно и не более одного запроса на отправителя; частичный успех сообщается по цепочке. dryRun: true выполняет те же чтения заголовков и дедупликацию и сообщает конечную точку, которую каждая цепочка wouldCall — ни с кем не связывается

snooze

Архивировать сейчас, всплыть в/после даты (YYYY-MM-DD), даты+времени (2026-06-20 9am) или предустановки (tomorrow, tomorrow 9am, weekend, next week, название дня недели, in N days, in N hours)

unsnooze

Отменить отсрочку, вернуть во входящие сейчас

list_snoozed

Все отложенные цепочки + даты выполнения

sweep_snoozed

Поднять цепочки, срок отсрочки которых истёк (запуск по требованию, через cron или демон); пакетно, с отчётом о частичных сбоях. dryRun: true отвечает «что должно быть поднято прямо сейчас?» (dueLabels/dueThreads), ничего не активируя.

list_filters

Все фильтры Gmail (критерии + действия с метками); отображает любые адреса forward в существующих фильтрах для аудита

create_filter

Создать серверное правило автосортировки (только критерии → действия с метками; без пересылки — см. ниже). Опционально applyToExisting для также обработки уже существующей в ящике почты, соответствующей критериям.

delete_filter

Удалить фильтр по id

Все инструменты объявляют outputSchema и возвращают структурированный контент (проверенный, машиночитаемый) вместе с тем же JSON в виде текста в кавычках — клиентам никогда не приходится разбирать прозу.

Как работает snooze (API Gmail snooze не существует — мы его создаем)

snooze удаляет INBOX и применяет датированную метку MCP/Snoozed/<key>, где ключ — это либо YYYY-MM-DD (на весь день), либо YYYY-MM-DDTHHMM (до указанной локальной минуты). Аргумент until принимает явную дату, дату+время (2026-06-20 9am, …T17:00) или предустановку, разрешаемую на сервере — today, tomorrow, weekend (следующая суббота), next week (следующий понедельник), название дня недели (mondaysunday, следующее вхождение), in N days или in N hours — и предустановка даты может содержать завершающее время (tomorrow 9am, monday 8:30), так что вызывающему никогда не приходится вычислять момент самостоятельно. sweep_snoozed находит просроченные метки и возвращает эти цепочки в папку «Входящие» (помечая как непрочитанные); отложенное пробуждение срабатывает при первом сканировании в/после указанной минуты, поэтому задержка пробуждения равна вашему интервалу сканирования. Запустите сканирование:

  • по требованию (инструмент sweep_snoozed),

  • через cron: mailwarden --sweep,

  • или автоматически: установите MAILWARDEN_AUTO_SWEEP=1 (ежечасное сканирование, пока сервер работает).

Фильтры (постоянные правила автоматической сортировки)

create_filter настраивает правило на стороне сервера Gmail: почта, соответствующая критериям, автоматически получает указанные действия с метками — почтовый ящик продолжает сортироваться сам без участия ассистента.

  • Критерии: from, to, subject, query (полный синтаксис поиска Gmail), negatedQuery, hasAttachment, excludeChats и size + sizeComparison (smaller/larger, указываются вместе). Требуется хотя бы один.

  • Действия (только метки): addLabels / removeLabels, по имени или идентификатору (неизвестное имя в addLabels создается автоматически, вложенное через /). Типичные рецепты: пропустить папку «Входящие» → removeLabels: ["INBOX"]; автоматически пометить как прочитанное → removeLabels: ["UNREAD"]; автоматически переместить в корзину → addLabels: ["TRASH"]; пометить звездочкой → addLabels: ["STARRED"]; никогда не в спам → removeLabels: ["SPAM"]; поместить под метку → addLabels: ["Receipts"].

  • Существующая почта: фильтр применяется только к сообщениям, поступившим после его создания. Передайте applyToExisting: true, чтобы также однократно применить те же действия к почте, уже находящейся в ящике — mailwarden строит поисковый запрос Gmail из критериев и выполняет массовое изменение (до maxMessages, по умолчанию 1000; те же оговорки о непроверенном индексе, что и для bulk_modify, и однократный проход исключает Спам/Корзину). Это требует хотя бы одного положительного критерия (from/to/subject/query/hasAttachment:true/size): правило, основанное только на исключениях (negatedQuery или hasAttachment:false), отклоняется для applyToExisting, поскольку оно соответствовало бы почти всему почтовому ящику — создавайте такой фильтр без этого флага. Результат возвращается в applied (использованный query, счетчики matchedMessages/modifiedMessages/modifiedThreadCount, capped, когда набор совпадений достиг maxMessages, failed по частям и строка error, если весь проход завершился неудачей); он равен null, когда applyToExisting не был установлен. Фильтр создается первым, поэтому частичный или неудачный проход по накопившимся сообщениям сообщается в applied, но никогда не вызывает ошибку — правило все равно действует.

  • Без пересылки — см. Безопасность и конфиденциальность.

  • Требует область gmail.settings.basic; повторно запустите --auth один раз, если вы авторизовали более старую версию. Недоступно в режиме только для чтения.

Отписка — единственный исходящий запрос

list_unsubscribe (уровень чтения) сообщает, что предлагает отправитель, не связываясь ни с кем. Он читает самое новое сообщение, которое действительно содержит заголовок List-Unsubscribe — ответ, привязанный к рассылке, находится в конце и ничего не рекламирует, что иначе было бы прочитано как «у этого списка нет возможности отказаться от подписки». list_subscriptions (уровень чтения) делает то же самое для целого среза, сгруппированного по отправителю, так что вы можете увидеть, кто продолжает писать и от кого из них можно на самом деле отказаться — один запрос заголовка на отправителя, а не на цепочку. unsubscribe и bulk_unsubscribe (уровень управления) действуют на основе этого — и это единственное место, где mailwarden когда-либо общается с хостом, не являющимся Google, поэтому правила строги:

  • Параметра URL нет. Конечная точка берется из собственного заголовка сообщения и ниоткуда больше. Аргумент URL позволил бы почте с внедренной подсказкой превратить инструмент в канал утечки (содержимое почтового ящика в строке запроса); заголовок не может нести данные, выбранные моделью.

  • Выполняется только одно нажатие RFC 8058 — отправитель должен был согласиться через List-Unsubscribe-Post. Обычная ссылка https: предназначена для человека в браузере и возвращается обратно, а не загружается.

  • Отказ от подписки через mailto: никогда не выполняется. Для этого потребовалось бы отправить письмо, чего mailwarden делать не умеет. Адрес сообщается, чтобы вы могли действовать самостоятельно.

  • Фиксированный запрос, отброшенный ответ. Тело POST всегда равно List-Unsubscribe=One-Click и никогда не выводится ни из чего; тело ответа отменяется как непрочитанное. Модели возвращается код состояния и фактически вызванный URL — никакого содержимого от конечной точки, поэтому она не может ответить инструкциями. (Перенаправление 301/302/303 выполняется как GET, т.е. вообще без тела.)

  • Один запрос на отправителя, последовательно, в рамках одного бюджета. bulk_unsubscribe принимает идентификаторы цепочек (никогда не запрос — запросный массовый запуск отправил бы запрос каждому соответствующему отправителю, прежде чем кто-либо посмотрел). Цепочки от отправителя, чей запрос уже был отправлен, сообщаются с duplicateOf и не требуют второго запроса: две цепочки из одного списка используют один отказ от подписки, и повторный вызов только дважды подтверждает ваш адрес. Отправитель записывается только после того, как запрос фактически достиг конечной точки, поэтому отказ или разрыв соединения все равно оставляют следующей цепочке свою собственную попытку — и если пропущенная цепочка рекламирует другую конечную точку, причина сообщает об этом, поскольку один отправитель может управлять несколькими списками. Ограничено 25 цепочками и 60 секундами на вызов; то, что бюджет не покрывает, возвращается как skippedOutOfTime, а не молча невыполненным. Ни одно из этих действий нельзя отменить, поэтому и существуют все три ограничения.

  • Защита SSRF. Только https, только порт по умолчанию, никаких учетных данных в URL, и каждый переход — включая перенаправления, не более 3 раз — должен разрешаться исключительно в глобально доступные адреса. Проверка разбирает каждый адрес в байты и сопоставляет его с реестром специального назначения IANA, поэтому все варианты написания одного и того же адреса получают одинаковый вердикт (::1 и 0:0:0:0:0:0:0:1 одинаково); адрес, который не разбирается, отклоняется. Разрешение DNS и все переходы имеют общий бюджет в 10 секунд. Не защищено от rebinding (fetch разрешается снова при подключении) — см. SECURITY.md; то, что переживает этот разрыв, представляет собой слепой POST, ответ на который никогда не читается.

Проверьте это на своей собственной почте, прежде чем доверять. Из клона репозитория (только репозиторий, не в пакете npm), после npm run build и mailwarden --auth:

node scripts/probe-unsubscribe.mjs --vet          # category:promotions, 25 threads
node scripts/probe-unsubscribe.mjs "from:substack.com" --max 50 --vet

Он печатает каждый реальный заголовок List-Unsubscribe рядом с тем, что из него извлек парсер, а --vet также прогоняет конечную точку через проверку URL и защиту адреса — так что вы видите и то, понял ли парсер заголовок, и то, пропустили бы защитные механизмы этот отказ от подписки. Строго только для чтения: запрос к отправителю никогда не выполняется, и ничего в почтовом ящике не меняется.

Что он не может отменить: запрос сообщает отправителю, что ваш адрес активен. Отправитель, который игнорирует собственный отказ от подписки, находится вне досягаемости любого клиента — для таких случаев комбинируйте unsubscribe с create_filter или trash. Отсутствие автоматизируемого варианта сообщается как unsubscribed:false с альтернативами, а не как ошибка. Развертывание в режиме read получает list_unsubscribe и list_subscriptions и никогда не выполняет запрос вообще.

Безопасность и конфиденциальность

Полную модель угроз — границы доверия, меры смягчения для каждой угрозы, явные нецели и способы сообщить об уязвимости — см. в SECURITY.md. Основные моменты:

  • Никакой телеметрии. Ничего не отправляется на домой — никакой аналитики, никаких отчетов о сбоях, никакого отслеживания.

  • Нет открытых портов по умолчанию. Только stdio. Опциональный слушатель --http привязывается к 127.0.0.1 (не к локальной сети) и отказывается запускаться без токена-носителя MAILWARDEN_TOKEN — установите MAILWARDEN_ALLOW_NO_TOKEN=1, чтобы переопределить это в доверенной изолированной сети. При привязке к loopback он также проверяет заголовок Host (защита от DNS-ребендинга). Для удаленного хостинга задайте MAILWARDEN_HOST и используйте TLS.

  • Нет инструментов отправки — намеренно. mailwarden не может составлять, отвечать или пересылать. Инструкция, внедренная в электронное письмо, не имеет пути для утечки через этот сервер. create_filter подчиняется тому же правилу: он может маркировать, архивировать, перемещать в корзину, помечать звездочкой или отмечать почту, но никогда не создает пересылающий фильтр (который был бы путем для утечки). list_filters по-прежнему показывает любые пересылающие фильтры, уже имеющиеся на аккаунте, так что вы можете их заметить. Это верно, потому что такого инструмента не существует, и ни один не может быть зарегистрирован во время выполнения; для более строгого варианта, где Google отказывается отправлять, а не mailwarden отклоняет, см. Режим только для чтения ниже.

  • Один исходящий хост, без URL, выбранного моделью. Инструмент unsubscribe — единственный путь кода, который связывается с хостом, не принадлежащим Google. Его конечная точка берется из заголовка List-Unsubscribe сообщения — никогда из аргумента инструмента — тело запроса фиксировано, а тело ответа отбрасывается, поэтому он не может стать каналом передачи данных. Только https/порт по умолчанию, перенаправления повторно проверяются, и любой переход, разрешающийся в частный, loopback, link-local или метаданные адрес, отклоняется. См. Отписка.

  • Уровни инструментов (постепенное раскрытие + наименьшая область). MAILWARDEN_TOOLS объявляет только те уровни, которые вы называете — read (инструменты чтения), manage (мутации почтового ящика, откладывание, загрузки), filters (CRUD серверных фильтров, единственный уровень, инструменты которого требуют gmail.settings.basic). По умолчанию все три; например, read,manage дает полную поверхность для триажа без управления фильтрами. Области действия OAuth, запрашиваемые при --auth, выводятся из включенных уровней — развертывание read запрашивает только gmail.readonly, а gmail.settings.basic запрашивается только при включенном уровне filters. И инструменты фильтрации скрываются автоматически, когда сохраненный токен не имеет gmail.settings.basic (например, токен, авторизованный до того, как вы включили уровень) — повторно запустите --auth, чтобы предоставить его. Старые токены без записанной области действия объявляются как прежде, с сообщением о недостаточной области действия во время выполнения в качестве запасного варианта.

  • Режим только для чтения. Установите MAILWARDEN_READONLY=1 (сокращение для MAILWARDEN_TOOLS=read), и будут зарегистрированы только инструменты чтения (search, get_thread, list_labels, list_snoozed, get_profile, triage_digest, list_unsubscribe, list_subscriptions) — клиентам не рекламируется ничего, что может изменить почтовый ящик или записать файлы (инструменты фильтрации, которым требуется более широкая область gmail.settings.basic, также исключены). Рекомендуется для общих/HTTP-развертываний, которые только выполняют триаж. Это также единственный уровень, свойство "не отправлять" которого обеспечивается Google: он содержит токен gmail.readonly, который конечные точки отправки Gmail категорически отклоняют. manage требует gmail.modify, и Gmail принимает эту область для отправки — mailwarden просто не предоставляет инструмента, который бы это сделал. Таким образом, развертывание read не могло бы отправлять, даже если бы этот бинарный файл был заменен; manage не может отправлять, потому что нечего вызывать. (Нет области записи без отправки, на которую можно было бы переключиться — см. SECURITY.md, угроза 1.)

  • Ограниченные загрузки. При установке MAILWARDEN_DOWNLOAD_DIR запись вложений ограничена этим каталогом (канонизирован по реальному пути, с учетом символических ссылок) и никогда не перезаписывает существующий файл.

  • Изоляция недоверенного контента. Каждый результат инструмента обернут в маркеры <untrusted-tool-output> и очищен от невидимых символов/символов двунаправленного переопределения, чтобы клиенты могли отличить цитируемое почтовое содержимое от инструкций.

  • Живой API, без копирования. Зеркало почтового ящика или поисковый индекс нигде не хранятся. Единственное локальное состояние — это ваш токен OAuth в ~/.mailwarden/.

  • Опциональное шифрование токена в покое. token.json содержит токен обновления; на диске он защищен только режимом mode 0o600 (бездействие на Windows). Установите MAILWARDEN_TOKEN_PASSPHRASE в виде ключевой фразы, и токен будет храниться зашифрованным с помощью AES-256-GCM (ключ, полученный через scrypt), так что копия файла — резервная копия, синхронизированная папка, другой компьютер — бесполезна без ключевой фразы. Повторно запустите mailwarden --auth после установки, чтобы зашифровать существующий токен. Обратите внимание на границу: это защищает от кражи файла, но не от вредоносного ПО, работающего от вашего имени пользователя (которое также может прочитать ключевую фразу из окружения).

Быстрый старт

claude mcp add mailwarden -- npx -y mailwarden

Это вся установка — npx загружает и запускает опубликованный пакет, без клонирования или сборки. Учетные данные Google OAuth нужны только один раз (ниже).

Настройка

Впервые настраиваете приложение Google OAuth? Следуйте пошаговому руководству по настройке — оно проведет вас через Google Cloud Console с точными путями кликов, объясняет экран "непроверенное приложение" и охватывает ловушку, из-за которой токены умирают через 7 дней. Краткая версия:

  1. Google Cloud: создайте проект → включите Gmail API → настройте экран согласия OAuth и опубликуйте его в Production (в статусе Тестирование Google аннулирует токены обновления через 7 дней) → создайте идентификатор клиента OAuth типа Десктопное приложение → загрузите его как credentials.json.

  2. Поместите credentials.json в ~/.mailwarden/ (или установите MAILWARDEN_CREDENTIALS=/путь/к/credentials.json).

  3. Авторизуйтесь один раз — откроется браузер, сохранит токен обновления в ~/.mailwarden/token.json:

    npx -y mailwarden --auth

    Запрашиваемые области: gmail.modify (чтение + метка/архив/корзина) и gmail.settings.basic (только управление фильтрами). Если вы авторизовали версию до появления фильтров, повторно запустите --auth один раз, чтобы предоставить добавленную область. Чтобы получить токен, с которым Gmail сам отказывается отправлять, авторизуйтесь с помощью MAILWARDEN_TOOLS=read — см. Режим только для чтения выше.

  4. Проверьте настройку в любое время с помощью встроенного доктора:

    npx -y mailwarden --check

    Он проверяет credentials.json, существует ли токен (и зашифрован ли он), покрывают ли предоставленные области ваши включенные уровни, и выполняет один живой вызов Gmail, чтобы доказать, что токен все еще работает — печатая конкретное исправление для всего, что не так, и завершаясь с ненулевым кодом, если это так (удобно в CI/проверках работоспособности). Диагностирует распространенные ловушки: отсутствие/неправильный файл учетных данных, никогда не авторизован, зашифрованный токен без MAILWARDEN_TOKEN_PASSPHRASE, отсутствующая область или истечение 7-дневного токена согласия "Тестирование".

Подключение

Claude Code (локальный stdio):

claude mcp add mailwarden -- npx -y mailwarden

Плагин Claude Code — тот же сервер плюс навык /mailwarden:setup, который проведет вас через настройку OAuth и диагностирует неработающую. Корень репозитория — это плагин (.claude-plugin/plugin.json), так что из клона:

claude --plugin-dir /path/to/mailwarden

Он отправлен в сообщество marketplace Anthropic; после публикации /plugin marketplace add anthropics/claude-plugins-community затем /plugin install mailwarden@claude-community делает то же самое без клона. Плагин запускает полную поверхность инструментов — для более узкого уровня (MAILWARDEN_TOOLS=read) или второй учетной записи используйте claude mcp add с нужным вам окружением (см. Конфигурация (env) и Несколько учетных записей).

Claude Desktop — добавьте в claude_desktop_config.json:

{
  "mcpServers": {
    "mailwarden": { "command": "npx", "args": ["-y", "mailwarden"] }
  }
}

Или установите пакет MCPB (mailwarden-<версия>.mcpb, прикрепленный к релизам GitHub начиная с версии 0.10.0) как расширение Desktop — Настройки → Расширения → Установить расширение… — тот же сервер, самодостаточный во время выполнения (без npx; Claude Desktop предоставляет среду выполнения Node), с уровнями инструментов в качестве настройки. Пакет собран из упакованного пакета npm (тот же набор файлов, что и опубликованный; npm run mcpb, проверено в CI: проверен, распакован и запущен) и представляет собой тот же набор файлов, который распространяет Smithery. Однократный npx -y mailwarden --auth все еще применяется (Node нужен один раз для этого) — пакет считывает тот же токен ~/.mailwarden/.

Smithery — указан как csitte/mailwarden, который обслуживает этот пакет:

npx -y @smithery/cli install csitte/mailwarden --client claude   # local stdio entry in the client's config

Обратите внимание, какой из двух путей Smithery вы выбираете. Приведенная выше установка записывает простую локальную запись сервера: процесс, ваш токен и ваша почта остаются на вашем компьютере, точно так же, как с npx. Добавление его в панель инструментов Smithery (smithery mcp add) также запускает пакет локально, но ретранслирует трафик инструментов через шлюз Smithery, так что удаленный клиент может достичь его — содержимое почтового ящика в этих ответах затем проходит через третью сторону. Это свойство шлюза, а не mailwarden; если вы хотите гарантию без третьих сторон, используйте локальную установку, пакет npm или .mcpb со страницы релиза.

Удаленно (Streamable HTTP) — для VPS / пользовательского коннектора claude.ai:

# Loopback + token required by default. For real hosting, bind outward and keep the token:
MAILWARDEN_TOKEN=<secret> MAILWARDEN_HOST=0.0.0.0 npx -y mailwarden --http   # :8787/mcp

Затем в claude.ai: Настройки → Коннекторы → Добавить пользовательский коннектор → URL вашего https://your-host/mcp. В Claude Code: claude mcp add --transport http mailwarden https://your-host/mcp.

Несколько учетных записей

Одно приложение OAuth (один credentials.json) может авторизовать несколько учетных записей Gmail. Каждая учетная запись хранит свой собственный токен обновления в отдельном файле, выбираемом с помощью MAILWARDEN_ACCOUNT:

mailwarden --auth --account work        # stores token.work.json
mailwarden --auth --account personal    # stores token.personal.json

Запускайте их бок о бок, регистрируя сервер по одному разу для каждой учетной записи, каждый со своим MAILWARDEN_ACCOUNT. Каждый экземпляр полностью изолирован — свой токен, свои предоставленные области, своя поверхность инструментов — так что ничто не может действовать от имени неправильного почтового ящика:

{
  "mcpServers": {
    "gmail-work":     { "command": "npx", "args": ["-y", "mailwarden"], "env": { "MAILWARDEN_ACCOUNT": "work" } },
    "gmail-personal": { "command": "npx", "args": ["-y", "mailwarden"], "env": { "MAILWARDEN_ACCOUNT": "personal" } }
  }
}

Имена учетных записей нечувствительны к регистру — они становятся именами файлов, так что Work и work будут одним и тем же файлом в Windows/macOS. mailwarden приводит их к нижнему регистру (--account Worktoken.work.json), так что имя всегда сопоставляется ровно с одним почтовым ящиком.

Какой файл записывает --auth, зависит только от --account / MAILWARDEN_ACCOUNT — никогда от учетной записи, которую вы выбираете в браузере. Авторизация второго почтового ящика без --account поэтому была бы нацелена прямо на файл токена первого, так что --auth сначала проверяет и отказывается, а не заменяет токен другого почтового ящика; --force намеренно переопределяет это. Два рычага не взаимозаменяемы: MAILWARDEN_ACCOUNT предназначен для нескольких почтовых ящиков из одного каталога конфигурации (он выбирает token.<имя>.json), тогда как MAILWARDEN_DIR перемещает весь каталог — полезно для полного разделения настроек, но не дает вам вторую учетную запись в одном. npm run auth из клона репозитория не передает ни то, ни другое, т.е. он всегда обслуживает учетную запись по умолчанию.

mailwarden --check показывает активную учетную запись и перечисляет другие, которые он находит. Если MAILWARDEN_ACCOUNT не задан, все использует файл по умолчанию token.json точно так же, как и раньше — это полностью обратно совместимо.

Из исходного кода

git clone https://github.com/csitte/mailwarden && cd mailwarden
npm install && npm run build
node dist/index.js --auth

Конфигурация (env)

Var

Значение

MAILWARDEN_DIR

каталог конфигурации (по умолчанию ~/.mailwarden)

MAILWARDEN_CREDENTIALS

путь к credentials.json

MAILWARDEN_ACCOUNT

выберите именованную учетную запись (ее токен — token.<имя>.json; имена приводятся к нижнему регистру); не задано = по умолчанию token.json. См. Несколько учетных записей

MAILWARDEN_TOKEN_PASSPHRASE

парольная фраза → шифрует token.json в состоянии покоя (AES-256-GCM); повторно запустите --auth после установки

MAILWARDEN_AUTO_SWEEP

1 → автоматическая очистка отложенных писем при запуске и ежечасно во время работы (записывает метки — требуется область manage/gmail.modify; предоставление только read не может выполнять очистку)

MAILWARDEN_DOWNLOAD_DIR

ограничить download_attachment этим каталогом (настоятельно рекомендуется для HTTP-хостинга)

MAILWARDEN_READONLY

1 → регистрировать только инструменты для чтения (search/get_thread/list_labels/list_snoozed/get_profile/triage_digest/list_unsubscribe/list_subscriptions). Сокращение для MAILWARDEN_TOOLS=read

MAILWARDEN_TOOLS

уровни инструментов, разделенные запятыми, для рекламы: read, manage, filters (по умолчанию: все). Также определяет области OAuth, запрашиваемые при --auth. Например, read,manage исключает инструменты фильтрации и их область gmail.settings.basic

MAILWARDEN_DEBUG

1 → выводить полные ошибки с трассировкой стека вместо однострочного сообщения (для отчетов об ошибках)

PORT

HTTP-порт (по умолчанию 8787)

MAILWARDEN_HOST

HTTP-адрес привязки (по умолчанию 127.0.0.1; укажите, например, 0.0.0.0 для удаленного хостинга)

MAILWARDEN_TOKEN

токен-носитель для HTTP-конечной точки — обязателен для --http, если не переопределен

MAILWARDEN_ALLOW_NO_TOKEN

1 → разрешить --http без токена (только для доверенных/изолированных сетей)

MAILWARDEN_ALLOWED_HOSTS

дополнительные значения host:port, разделенные запятыми, принимаемые белым списком Host для loopback

Статус

Работает и используется в ежедневной автоматизации почтового ящика. Основные инструменты Gmail + отложенные письма реализованы на googleapis, покрыты набором тестов vitest (789 тестов — npm run coverage). Текущая версия: см. значок npm выше, журнал изменений или релизы. PR приветствуются.

Лицензия

MIT © C.Sitte Softwaretechnik

Install Server
A
license - permissive license
A
quality
A
maintenance

Maintenance

Maintainers
Response time
2dRelease cycle
24Releases (12mo)
Commit activity

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    An MCP server that enables Gmail integration, allowing users to manage emails (send, receive, read, trash, mark as read) directly through MCP clients like Claude Desktop.
    1
    MIT
  • A
    license
    B
    quality
    D
    maintenance
    Manage your emails effortlessly with a standardized interface for drafting, sending, retrieving, and organizing messages. Streamline your email workflow with complete Gmail API coverage, including label and thread management.
    64
    1,398
    56
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Gmail MCP server — scope-gated tools (readonly / send / modify), path jails for attachments + downloads, hardened OAuth credentials, Sigstore-signed releases.
    207
    11
    MIT
  • A
    license
    A
    quality
    F
    maintenance
    A Gmail MCP server with native multi-account support, enabling management of multiple Gmail accounts from a single server instance.
    7
    5
    MIT

View all related MCP servers

Related MCP Connectors

  • Read, search, send, organize, draft and schedule email across your inboxes from any MCP client.

  • Shipmail MCP server for AI agent custom-domain email inboxes with REST API and webhooks.

  • Fully-managed email as MCP tools - register domains, real mailboxes, send and receive mail.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/csitte/mailwarden'

If you have feedback or need assistance with the MCP directory API, please join our Discord server