mailwarden
mailwarden
신뢰할 수 있는 네이티브 Gmail MCP 서버 — AI 어시스턴트를 위한 완전한 받은편지함 관리 기능을 제공하며, 다른 Gmail MCP 서버가 제공하지 않는 메일함 기반 스누즈 기능을 탑재했습니다.
주요 기능
스누즈 — Gmail MCP 서버 중 유일한 메일함 기반 스누즈. 지금 스레드를 보관하고, 지정한 날짜에 받은편지함에 다시 표시되도록 합니다. 날짜 레이블과 스윕(sweep)을 기반으로 구축되어 모든 클라이언트에서 작동하며, Gmail 자체에서도 확인 가능하고, 재시작 후에도 유지됩니다. (다른 서버가 "스누즈"를 제공하는 경우, 이는 로컬 알림 목록일 뿐입니다 — 메일은 받은편지함을 떠나거나 다시 들어오지 않습니다.)
신뢰할 수 있는 검색. Gmail의
threads.list— 모든 스레드 검색이 거치는 호출 — 는 오래된 스레드 수준 읽기 상태로부터is:unread에 답할 수 있습니다. 실제 메일함에서 측정한 결과, 반환된 스레드 중 86%가 읽지 않은 메시지를 하나도 포함하지 않았습니다; 두 번째 메일함에서는 전혀 오차가 없었습니다. 확인하지 않고는 어느 메일함에 있는지 알 수 없으므로,search는 모든 검색 결과를 실시간 레이블과 다시 확인합니다.pageToken/nextPageToken을 통해 페이지네이션됩니다.확장 가능한 대량 작업.
bulk_modify는 쿼리와 일치하는 모든 항목을 API 요청당 1000개의 메시지로 보관/레이블 지정합니다 — 전부 아니면 전무(all-or-nothing) 방식 대신 청크별 부분 성공 보고를 제공합니다. 스누즈 스윕도 동일한 배치 경로를 사용합니다.구조화된 출력. 모든 도구는
outputSchema를 선언하고, 검증된structuredContent를 펜싱된 JSON 텍스트와 함께 반환합니다 — 클라이언트의 파싱 추측이 필요 없습니다.작은 공격 표면. 전송 도구 없음 (프롬프트 인젝션된 메일의 유출 경로 없음), 선택적 읽기 전용 모드, 원격 측정 없음, 기본적으로 열린 포트 없음, 심볼릭 링크로부터 안전한 다운로드 차단, 인젝션 차단 출력. 의도적인 예외 하나:
unsubscribe/bulk_unsubscribe(관리 등급)는 메시지 자체 헤더에 명시된 수신 거부 엔드포인트에 접속합니다 — mailwarden이 도달하는 유일한 비(非)Google 호스트이며,read등급 배포는 아웃바운드 요청을 전혀 하지 않습니다. 자세한 내용은 보안 및 개인정보 보호 및 수신 거부를 참조하세요.실제 메일과의 정확한 호환성. RFC 2047 헤더 디코딩 (
=?UTF-8?B?…?=→ 읽을 수 있는 텍스트), 본문은 선언된 문자셋으로 디코딩 (ISO-8859-1/Shift_JIS 메일의 경우 모지바케(mojibake) 없음), 429/5xx 오류는 지수 백오프로 재시도.
Related MCP server: Gmail MCP
이유
사서함을 동기화하거나 캐싱하는 커넥터는 실제 사서함보다 뒤처질 수 있습니다 — 심지어 Gmail 자체 검색 인덱스도 때로는 느슨합니다 (아래 참조). mailwarden은 실시간 Gmail API와 직접 통신하며 (캐시된 스냅샷 없음), 인덱스가 반환한 내용을 재확인하므로, 보이는 것이 실제로 존재하는 것입니다. 이는 일반적인 Gmail 기능 계층입니다 — 자신의 규칙/로직은 AI 클라이언트에 유지하고, 서버에는 두지 마세요.
search는 원시 API보다 한 단계 더 나아갑니다: Gmail의 threads.list 인덱스는 해당 상태의 오래된 복사본으로부터 읽기 상태 연산자에 답할 수 있으므로, is:unread는 몇 주 전에 다 읽은 스레드를 반환합니다 — 측정된 한 메일함에서는 반환된 결과의 대다수가 그랬습니다. 모든 검색 결과는 어차피 실시간으로 가져오기 때문에, search는 명확한 조건자 (is:unread/is:read/is:starred/in:inbox/category:…, 부정 포함)를 각 스레드의 실제 레이블과 재확인하여 인덱스의 거짓 양성을 제거합니다.
다른 Gmail MCP 서버와의 비교
대부분의 Gmail MCP 서버는 동일한 읽기/레이블/전송 표면을 다룹니다. mailwarden에만 여전히 고유한 두 가지 기능(메일함 기반 스누즈, 검색 재확인)이 있으며, 하나의 의도적인 생략은 보안 기능이지 결함이 아닙니다. Google의 자체 서버도 보기보다 좁습니다: 초안 전용이며, 휴지통, 필터 또는 수신 거부 기능이 없습니다.
기능 | mailwarden | ||||
메일함 기반 스누즈 — 지금 보관하고, 지정된 날짜/시간 또는 사전 설정에 받은편지함에 다시 표시 | ✅ | — | — | — | — |
검색 결과 재확인 — 실시간 레이블과 대조하여 스레드 인덱스의 거짓 양성 제거 | ✅ | — | — | — | — |
쿼리 기반 스윕/대량 작업 — 검색 결과의 모든 스레드에 대해 한 번의 작업 | ✅ 1000/요청, 부분 성공 | — | ⚠️ 명시적 ID로 배치 | — | ⚠️ 명시적 ID로 배치 |
수신 거부 — 발신자별 개요 + RFC 8058 원클릭 수신 거부, 전송 범위 불필요 | ✅ | — | ⚠️ 헤더 표시, 작업 없음 | — | — |
받은편지함 분류 개요 — 대기 중인 항목을 한 번의 호출로 분류 | ✅ 발신자/레이블/기간 + 헤더 신호 | — | — | ✅ 휴리스틱 플래그 + 통계 | — |
서버 측 필터 — 어시스턴트 없이도 지속적으로 분류하는 규칙 | ✅ 전달 금지 | — | ✅ | — | ✅ |
전송 도구 없음 — 설계상 — 프롬프트 인젝션된 메일의 유출 경로 없음 | ✅ 작성 기능 전혀 없음 | ⚠️ 초안 전용 | ❌ 전송 | ❌ 전송 | ❌ 전송 |
최소 권한 도구 등급 — 활성화한 도구에서 파생된 OAuth 범위 | ✅ | ⚠️ 범위 분할 | — | — | ⚠️ 역방향: 부여된 범위에 의해 제한되는 도구 |
저장 시 토큰 암호화 (선택 사항) | ✅ AES-256-GCM | 해당 없음 (호스팅) | ✅ | — | — |
벤더 클라우드 없음 — 사용자가 서버 운영 | ✅ | ❌ Google 호스팅 | ✅ | ✅ | ✅ |
구조화된 출력 — 모든 도구가 | ✅ | — | — | — | — |
2026년 8월 16일 기준, 각 프로젝트의 공개 문서 및 소스에서 가져옴; — = 제공되지 않음 / 문서화되지 않음. 열은 독자가 가장 먼저 접할 가능성이 높은 서버들 — Google의 자사 서버와 가장 큰 두 커뮤니티 서버 — 그리고 mailwarden의 최소 권한 설계에 가장 가까운 klodr입니다. 전송 기능은 보안 속성으로 나열됩니다: mailwarden에 이 기능이 없는 것은 의도적입니다 (보안 및 개인정보 보호 참조). 마지막 행은 서버가 어디서 실행되는지가 아니라 누가 운영하는지를 묻습니다: 자체 호스팅은 여기서 공통된 기반이며, 이 표의 모든 커뮤니티 서버는 klodr(stdio 전용)을 제외하고 일부 원격 배포를 제공합니다 — mailwarden은 --http를 통해, taylorwilsdon은 OAuth 2.1을 사용한 스트리밍 가능 HTTP를 통해, a-bonus는 Cloud Run에서 실행됩니다. 자체 호스트에서 실행하는 것은 클라우드 복사본이 아닙니다; 벤더의 호스트에서 실행하는 것이 클라우드 복사본입니다.
핵심 우위는 단일 행이 아닙니다 — 스누즈 + 실시간 재확인의 결합입니다: 캐시된 스냅샷이 아닌 메일함의 현재 상태에 대해 작동하는 실제 받은편지함 워크플로우 계층입니다. 다른 서버가 따라잡은 부분은 위에 정직하게 명시되어 있습니다: 저장 시 암호화 (taylorwilsdon), 범위 기반 도구 게이팅 (klodr), 더 풍부한 메시지별 분류 휴리스틱 (a-bonus), 그리고 메일함 전체 대량 정리 (호스팅된 mcpemails.com, 스누즈 없음). 그들 중 어느 것도 쿼리에 대해 작동하고, 그에 따라 행동하기 전에 메일함의 답변을 확인하지 않습니다.
재확인이 중요한 이유 — 구체적인 사례
어시스턴트에게 "이미 내 받은편지함을 건너뛴 읽지 않은 프로모션 메일을 보관해" 라고 요청하면, 명백한 쿼리인 category:updates is:unread -in:inbox를 사용할 것입니다. Gmail의 인덱스를 신뢰하는 서버는 이제 이미 읽은 스레드를 보관하게 됩니다 — 건드릴 의도가 전혀 없었던 메일이, 쉽게 되돌릴 수 없는 대량 작업으로 사라집니다.
측정된 결과, 주장이 아닙니다. 하나의 실제 메일함(약 70,000개 메시지), 2026년 8월 15일, 읽기 전용:
쿼리 ( | 반환된 스레드 | 읽지 않은 메시지가 있는 스레드 | 부실 |
| 131 | 17 | 87% |
| 128 | 14 | 89% |
| 235 | 99 | 58% |
인덱스는 해당 조건을 무시하지 않습니다. is:unread가 없는 동일한 쿼리는 800개 이상의 스레드를 반환하므로, 조건이 적용되고 있습니다. 하지만 이는 따라잡지 못한 스레드 수준의 읽음 상태에 대해 적용됩니다: 모든 메시지가 읽혔음에도 여전히 읽지 않은 것으로 간주되는 스레드가 있습니다. 반환된 한 스레드에는 단일 레이블 SENT만 있었습니다. 그리고 이것은 특이한 연산자 조합의 기이한 현상이 아닙니다: 세 가지 중 가장 단순한 쿼리에서도 동일하게 나타납니다 — 가장 낮은 비율(58%)이지만 절대적인 수치로는 가장 많은 잘못된 스레드(136개)입니다.
특히 스레드 인덱스의 문제입니다. 동일한 쿼리, 동일한 사서함, 동일한 시간에 messages.list를 통해 요청한 경우: 19개의 메시지, 부실 없음. 따라서 이것은 "Gmail 검색이 신뢰할 수 없다"는 문제가 아니라, 스레드 관점의 읽음 상태가 지연되는 반면, 개별 메시지 관점은 그렇지 않다는 것입니다. search는 threads.list를 통해 처리되며, 이것이 바로 다시 검증하는 이유입니다.
동일한 방식으로 같은 날 측정한 두 번째 사서함은 전혀 표류하지 않았습니다. — is:unread에 대한 원시 인덱스 히트가 0건이었지만, API를 통해 하루에도 여러 번 읽음 표시가 이루어졌습니다. 따라서 이것은 모든 Gmail의 속성이 아니라 하나의 사서함의 속성입니다. 이들을 구분하는 요인은 알 수 없습니다. 두 사서함은 볼륨(약 3자리수 차이)과 수명에서 차이가 나며, 두 번째 사서함에는 더 기본적인 것이 누락되어 있습니다. 즉, 그 사서함의 스레드 중 읽지 않은 상태로 보관된 적이 없습니다. 이는 부실한 읽음 상태가 나타날 수 있는 유일한 형태입니다. 따라서 이것은 특정 원인에 대한 반례가 아니라, 해당 조건을 충족하지 않는 사서함일 뿐입니다.
이것이 핵심입니다: 서버는 자신이 어떤 종류의 사서함에 있는지 알 수 없습니다. 표류가 없는 곳에서는 재검증 비용이 들지 않으며, 표류가 있는 곳에서는 문제를 해결해 줍니다. 위의 측정에서 search가 제거한 모든 스레드는 실제로 읽은 것이었으며, 실제로 읽지 않은 메일은 하나도 폐기하지 않았습니다.
비용이 무료가 아닌 경우: 대량 도구. search는 어차피 모든 히트를 가져오기 때문에 재검증합니다. bulk_modify(및 create_filter의 applyToExisting 스윕)는 수천 개의 메시지 단위로 크기가 조정되며, 히트당 한 번의 가져오기는 다른 비용 차원입니다. 이 도구들은 인덱스가 반환한 결과에 대해 작동합니다. 따라서 이제 unverifiedPredicates를 보고합니다. 이는 인덱스의 말을 그대로 받아들인 쿼리의 조건들(+UNREAD, -INBOX, …)입니다. 비어 있으면 불신할 것이 없음을 의미합니다. 비어 있지 않고 결과가 읽음 상태에 정확해야 한다면? 먼저 search로 집합을 확인한 후 해당 스레드 ID에 대해 작업하십시오. 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에 대해 실행합니다. 이 API의 인덱스는 의도적으로 부실합니다(정확히 Gmail이 하는 것처럼 is:unread 쿼리에 대해 읽은 스레드를 반환). 그리고 mailwarden이 잘못된 긍정을 제거하는 것을 보여줍니다. 결과를 단언(assert)하므로, 동작이 회귀하면 0이 아닌 종료 코드를 반환합니다. 동일한 사례는 test/gmail.test.ts의 단위 테스트에 의해 고정되어 있습니다 ("실시간 레이블 재확인을 통해 인덱스의 잘못된 긍정 제거").
도구
도구 | 기능 설명 |
| Gmail 쿼리 구문 → 스레드 요약 (보낸사람/제목/날짜/라벨/스니펫); 읽음 상태/카테고리 조건은 각 결과의 실시간 라벨과 다시 확인됨; |
| 전체 스레드: 헤더, 일반 텍스트 + HTML 본문, 첨부 파일 메타데이터 |
| 모든 라벨 (시스템 + 사용자) |
| 연결된 계정의 주소 + 총 메시지/스레드 수 — 작업 전에 어느 사서함이 연결되었는지 확인 |
| 의사 결정을 위한 사서함 슬라이스의 구조적 개요: 상위 발신자 (각각 해당 스레드가 가진 신호 포함), 라벨 및 기간 버킷, 읽지 않음 + 첨부 파일 수, 뉴스레터/자동화/캘린더 초대/회신 주소 불일치 스레드 수 — 원시 스레드 목록 대신 |
| 스레드가 광고하는 수신 거부 옵션 ( |
| 발신자별로 그룹화된 사서함 슬라이스: 스레드/읽지 않음 수, 각각이 관찰된 날짜 범위, 각각의 수신 거부 옵션 — 발신자당 하나의 헤더 가져오기, 외부 연락 없음. |
| 사용자 라벨 생성 (멱등성; |
| 이름 또는 id로 라벨 추가/제거 — |
| 쿼리와 일치하는 모든 메시지에 대한 배치 라벨 변경 — API 요청당 1000개 메시지, 청크별 부분 성공 보고 (스레드 ID 목록은 500개로 제한, |
| 편의 래퍼 |
| 휴지통으로 이동 / 휴지통에서 복원 |
| 첨부 파일을 로컬 경로에 저장 (덮어쓰지 않음 — 충돌 시 숫자 접미사 추가) |
| 메시지 자체 헤더의 엔드포인트를 사용한 원클릭 수신 거부 (RFC 8058) — 비 Google 호스트에 연락하는 유일한 도구 (세부 정보) |
| 여러 스레드에 대해 동일한 작업을 순차적으로 수행하며 발신자당 최대 하나의 요청; 스레드별 부분 성공 보고. |
| 지금 보관, 날짜( |
| 스누즈 취소, 지금 받은편지함으로 복귀 |
| 모든 스누즈된 스레드 + 기한 |
| 스누즈 기한이 지난 스레드 다시 표시 (요청 시, cron을 통해, 또는 데몬으로 실행); 배치 처리, 부분 실패 보고 포함. |
모든 도구는 outputSchema를 선언하고 구조화된 콘텐츠(검증된, 기계 판독 가능)를 반환하며, 동일한 JSON을 펜스 처리된 텍스트로 함께 제공합니다. 클라이언트는 산문을 구문 분석할 필요가 없습니다.
snooze 작동 방식 (Gmail API에 snooze가 없음 — 직접 구현)
snooze는 INBOX를 제거하고 날짜가 지정된 레이블 MCP/Snoozed/<key>를 적용합니다. 여기서 키는 YYYY-MM-DD(하루 종일) 또는 YYYY-MM-DDTHHMM(해당 로컬 분에 만료)입니다. until 인수는 명시적 날짜, 날짜+시간(2026-06-20 9am, …T17:00), 또는 서버 측에서 해석되는 프리셋(today, tomorrow, weekend(다음 토요일), next week(다음 월요일), 요일 이름(monday–sunday, 다음 발생), in N days, in N hours)을 받습니다. 날짜 프리셋에는 시간이 뒤따를 수 있습니다(tomorrow 9am, monday 8:30). 따라서 호출자가 직접 시간을 계산할 필요가 없습니다. sweep_snoozed는 만료된 레이블을 찾아 해당 스레드를 받은편지함으로 반환합니다(읽지 않음으로 표시). 시간 지정 snooze는 해당 분 이후 첫 번째 스윕에서 깨어나므로, 깨어나는 지연 시간은 스윕 간격과 같습니다. 스윕 실행:
요청 시(
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, 이름 또는 ID로 지정 (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개수, 일치 집합이maxMessages에 도달하면capped, 청크별failed, 전체 패스 실패 시error문자열).applyToExisting이 설정되지 않은 경우null입니다. 필터가 먼저 생성되므로, 부분적이거나 실패한 백로그 패스는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는 스레드 ID를 받습니다(절대 쿼리가 아님 — 쿼리 기반 대량 작업은 누군가 확인하기 전에 일치하는 각 발신자에게 요청을 보냅니다). 이미 요청이 나간 발신자의 스레드는duplicateOf로 보고되며 두 번째 요청이 필요하지 않습니다. 하나의 목록에서 두 스레드는 옵트아웃을 공유하며, 두 번 호출하면 주소를 두 번 확인할 뿐입니다. 발신자는 요청이 실제로 엔드포인트에 도달한 경우에만 기록되므로, 거부나 연결 끊김이 발생해도 다음 스레드가 자체 시도를 할 수 있습니다. 건너뛴 스레드가 다른 엔드포인트를 광고하는 경우, 이유가 그렇게 표시됩니다. 하나의 발신자가 여러 목록을 운영할 수 있기 때문입니다. 호출당 최대 25개 스레드 및 60초로 제한됩니다. 예산이 충당하지 못하는 것은 조용히 취소되지 않고skippedOutOfTime으로 반환됩니다. 이 중 어떤 것도 되돌릴 수 없으므로 세 가지 제한이 모두 존재합니다.SSRF 보호. https만, 기본 포트만, URL에 자격 증명 없음, 그리고 모든 홉(최대 3회 리디렉션 포함)은 전역적으로 도달 가능한 주소로만 확인되어야 합니다. 검사는 각 주소를 바이트로 구문 분석하고 IANA 특수 목적 레지스트리와 대조하므로, 동일한 주소의 모든 표기는 동일한 판정을 받습니다(
::1과0:0:0:0:0:0:0:1도 동일). 구문 분석되지 않는 주소는 거부됩니다. DNS 확인 및 모든 홉은 하나의 10초 예산을 공유합니다. 리바인딩 방지 기능이 없습니다(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(LAN이 아님)에 바인딩되며MAILWARDEN_TOKEN베어러 토큰 없이는 시작을 거부합니다 — 신뢰할 수 있는 격리된 네트워크에서는MAILWARDEN_ALLOW_NO_TOKEN=1을 설정하여 재정의할 수 있습니다. 루프백 바인딩 시Host헤더도 검증합니다(DNS-리바인딩 방어). 원격 호스팅의 경우MAILWARDEN_HOST를 설정하고 TLS로 보호하세요.설계상 전송 도구 없음. mailwarden은 작성, 회신, 전달할 수 없습니다. 이메일 내에 프롬프트 주입된 명령어는 이 서버를 통해 유출될 경로가 없습니다.
create_filter도 동일한 규칙을 따릅니다: 메일에 레이블 지정, 보관, 휴지통, 별표 표시 또는 표시는 할 수 있지만 절대 전달 필터(유출 경로가 될 수 있음)를 생성하지 않습니다.list_filters는 계정에 이미 있는 전달 필터를 계속 표시하므로 이를 발견할 수 있습니다. 이는 그러한 도구가 존재하지 않고 런타임에 등록될 수 없기 때문에 유지됩니다. mailwarden이 거부하는 대신 Google이 전송을 거부하는 더 강력한 변형에 대해서는 아래 읽기 전용 모드를 참조하세요.하나의 아웃바운드 호스트, 모델이 선택한 URL 없음.
unsubscribe도구는 Google이 아닌 호스트에 접촉하는 유일한 코드 경로입니다. 해당 엔드포인트는 메시지의List-Unsubscribe헤더에서 읽어옵니다 — 도구 인수에서 절대 가져오지 않음 — 요청 본문은 고정되어 있고 응답 본문은 폐기되므로 데이터 채널이 될 수 없습니다. https/기본 포트만 허용, 리디렉션은 재검증되며, 개인, 루프백, 링크-로컬 또는 메타데이터 주소로 확인되는 모든 홉은 거부됩니다. 구독 취소를 참조하세요.도구 계층(점진적 공개 + 최소 범위).
MAILWARDEN_TOOLS는 사용자가 지정한 계층만 광고합니다 —read(읽기 도구),manage(사서함 변경, 스누즈, 다운로드),filters(서버 측 필터 CRUD, 도구에gmail.settings.basic이 필요한 유일한 계층). 기본값은 세 가지 모두입니다. 예:read,manage는 필터 관리 없이 전체 분류 표면을 제공합니다.--auth에서 요청되는 OAuth 범위는 활성화된 계층에서 파생됩니다 —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이 설정되면 첨부 파일 쓰기는 해당 디렉토리(realpath-정규화, 심볼릭 링크 인식)로 제한되며 기존 파일을 덮어쓰지 않습니다.신뢰할 수 없는 콘텐츠 격리. 모든 도구 결과는
<untrusted-tool-output>마커로 감싸지고 보이지 않는/BiDi-재정의 문자가 제거되어 클라이언트가 인용된 메일 콘텐츠와 명령어를 구분할 수 있습니다.라이브 API, 복사본 없음. 사서함 미러나 검색 인덱스는 어디에도 저장되지 않습니다. 유일한 로컬 상태는
~/.mailwarden/에 있는 OAuth 토큰입니다.선택적 저장 시 토큰 암호화.
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일 후에 만료되게 하는 함정을 다룹니다. 간단한 버전:
Google Cloud: 프로젝트 생성 → Gmail API 활성화 → OAuth 동의 화면 구성 및 프로덕션에 게시(테스트 상태에서는 Google이 갱신 토큰을 7일 후에 만료시킴) → 데스크톱 앱 유형의 OAuth 클라이언트 ID 생성 →
credentials.json으로 다운로드.credentials.json을~/.mailwarden/에 넣습니다(또는MAILWARDEN_CREDENTIALS=/path/to/credentials.json설정).한 번 승인합니다 — 브라우저가 열리고, 갱신 토큰이
~/.mailwarden/token.json에 저장됩니다:npx -y mailwarden --auth요청되는 범위:
gmail.modify(읽기 + 레이블/보관/휴지통) 및gmail.settings.basic(필터 관리 전용). 필터가 존재하기 전에 승인한 경우,--auth를 한 번 다시 실행하여 추가된 범위를 부여하세요. Gmail 자체가 전송을 거부하는 토큰을 보유하려면MAILWARDEN_TOOLS=read로 승인하세요 — 위의 읽기 전용 모드 참조.설정을 확인하려면 언제든지 내장된 진단 도구를 사용하세요:
npx -y mailwarden --checkcredentials.json, 토큰 존재 여부(및 암호화 여부), 부여된 범위가 활성화된 계층을 포함하는지 확인하고, 하나의 라이브 Gmail 호출을 수행하여 토큰이 여전히 작동하는지 증명합니다 — 잘못된 사항에 대한 구체적인 수정 사항을 출력하고, 문제가 있으면 0이 아닌 종료 코드를 반환합니다(CI/상태 확인에 유용). 일반적인 함정을 진단합니다: 자격 증명 파일 없음/잘못됨, 승인되지 않음,MAILWARDEN_TOKEN_PASSPHRASE없는 암호화된 토큰, 누락된 범위, 또는 7일 "테스트" 동의 토큰 만료.
연결
Claude Code(로컬 stdio):
claude mcp add mailwarden -- npx -y mailwardenClaude Code 플러그인 — 동일한 서버에 OAuth 설정을 안내하고 문제를 진단하는 /mailwarden:setup 스킬이 추가되었습니다. 저장소 루트가 플러그인(.claude-plugin/plugin.json)이므로, 클론에서:
claude --plugin-dir /path/to/mailwardenAnthropic의 커뮤니티 마켓플레이스에 제출되었습니다. 등록되면 /plugin marketplace add anthropics/claude-plugins-community 후 /plugin install mailwarden@claude-community를 실행하면 클론 없이 동일한 작업을 수행합니다. 플러그인은 전체 도구 표면을 실행합니다 — 더 좁은 계층(MAILWARDEN_TOOLS=read)이나 두 번째 계정의 경우, 원하는 환경으로 claude mcp add를 사용하세요(구성(환경 변수) 및 여러 계정 참조).
Claude Desktop — claude_desktop_config.json에 추가:
{
"mcpServers": {
"mailwarden": { "command": "npx", "args": ["-y", "mailwarden"] }
}
}또는 MCPB 번들(mailwarden-<version>.mcpb, 0.10.0부터 GitHub 릴리스에 첨부)을 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 configSmithery의 두 경로 중 어느 것을 선택하는지 유의하세요. 위의 설치는 일반 로컬 서버 항목을 작성합니다: 프로세스, 토큰 및 메일은 npx와 정확히 동일하게 사용자 머신에 남아 있습니다. 대신 Smithery의 도구 상자에 추가(smithery mcp add)하면 번들을 로컬에서 실행하지만, 도구 트래픽을 Smithery의 게이트웨이를 통해 중계하여 원격 클라이언트가 도달할 수 있게 합니다 — 해당 응답의 사서함 콘텐츠는 제3자를 통과합니다. 이는 게이트웨이의 속성이며 mailwarden의 속성이 아닙니다. 제3자 없는 보장을 원한다면 로컬 설치, 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에서: 설정 → 커넥터 → 사용자 정의 커넥터 추가 → https://your-host/mcp URL. 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 Work → token.work.json) 따라서 이름은 항상 정확히 하나의 사서함에 매핑됩니다.
--auth가 쓰는 파일은 --account / MAILWARDEN_ACCOUNT에만 의존하며 브라우저에서 선택한 계정에는 절대 의존하지 않습니다. 따라서 --account 없이 두 번째 사서함을 승인하면 첫 번째 사서함의 토큰 파일을 직접 대상으로 하게 되므로, --auth는 먼저 확인하고 다른 사서함의 토큰을 교체하는 것을 거부합니다. --force는 의도적으로 재정의합니다. 두 노브는 상호 교환 가능하지 않습니다: MAILWARDEN_ACCOUNT는 하나의 구성 디렉토리에서 여러 사서함을 위한 것입니다(token.<name>.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구성(환경 변수)
Var | Meaning |
| 설정 디렉토리 (기본값 |
|
|
| 이름이 지정된 계정 선택 (토큰은 |
| 암호 → |
|
|
|
|
|
|
| 쉼표로 구분된 광고할 도구 계층: |
|
|
| HTTP 포트 (기본값 8787) |
| HTTP 바인드 주소 (기본값 |
| HTTP 엔드포인트의 베어러 토큰 — 재정의되지 않으면 |
|
|
| 루프백 |
상태
작동 중이며 일일 메일함 자동화에 사용 중입니다. 핵심 Gmail 도구 + 스누즈가 googleapis를 기반으로 구현되었으며, vitest 테스트 스위트로 커버됨 (789개 테스트 — npm run coverage). 현재 버전: 위의 npm 배지, 변경 로그 또는 릴리스를 참조하세요. PR 환영합니다.
라이선스
MIT © C.Sitte Softwaretechnik
Maintenance
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceAn 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.1MIT
- AlicenseBqualityDmaintenanceManage 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.641,39856MIT
- AlicenseNot gradedqualityAmaintenanceGmail MCP server — scope-gated tools (readonly / send / modify), path jails for attachments + downloads, hardened OAuth credentials, Sigstore-signed releases.20711MIT
- AlicenseAqualityFmaintenanceA Gmail MCP server with native multi-account support, enabling management of multiple Gmail accounts from a single server instance.75MIT
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.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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