big-mailer
big-mailer 📬
브로드캐스트, 드립 시퀀스, 트랜잭셔널 이메일을 하나의 Cloudflare Worker에서. 유료 ESP를 대체하는 자체 호스팅 솔루션으로, 목록과 발송, 참여 데이터가 모두 당신의 것이 됩니다.
상태: 기능 완성 및 로컬 실행 가능, 아직 배포 전. 단일 운영자를 위해 만들어졌으며 의도적으로 멀티 테넌트가 아닙니다(누락이 아니라 설계상). 남은 부분에 대한 솔직한 목록은 배포 전에를 참조하세요.

데모 데이터 시딩 후의 대시보드. 스코프별 동의가 핵심 패널입니다: 두 명이 개별 시리즈를 탈퇴했지만 여전히 목록에 남아 있습니다. 일반 ESP에서는 이 두 숫자가 같은 숫자입니다.
아이디어 💡
모든 ESP는 구독 해지를 하나의 스위치로 취급합니다. 누군가 온보딩 시리즈를 마치고 "구독 해지"를 클릭해 그것만 중단하면, 조용히 뉴스레터까지 떠나버립니다. 당신은 그 사실을 알 수 없습니다. 숫자만 줄어들 뿐입니다.
여기서 동의는 스코프별로 구분됩니다. 하나의 시리즈를 떠나는 것은 그 시리즈에서만 제외됩니다. 뉴스레터를 떠나는 것은 의도적으로 선택한 시리즈를 취소하지 않습니다. "모든 것에서 구독 해지"를 명시적으로 요청하거나, 하드 바운스, 또는 스팸 신고가 있어야만 완전히 제거됩니다.
이 비대칭성이 이 프로젝트가 존재하는 이유이며, 코드베이스의 다른 모든 것은 우연히 깨질 수 없도록 구성되어 있습니다.
Related MCP server: Resend MCP Server
실행하기 🚀
bun install
bun run db:migrate # applies migrations to the local D1 database
bun run dev # http://localhost:8787http://localhost:8787을 열고 데모 데이터 시딩을 클릭하세요: 12명의 사람, 2개의 라이브 시리즈, 1개의 발송된 브로드캐스트. 그런 다음 Outbox를 열어 "발송된" 메일을 읽어보세요.
아무것도 당신의 머신을 떠나지 않습니다. EMAIL_PROVIDER=console이 로컬 기본값이며
완전히 렌더링된 메일을 실제 발송 대신 앱 내 Outbox에 기록합니다. API 키가 필요 없고,
만지작거리는 동안 실수로 실제 사람에게 메일을 보낼 방법도 없습니다.
이 프로젝트의 목적을 직접 확인하기 🎯
구독자 → 한 명을 선택 → 선호도 센터 열기
해당 URL에
?scope=sequence:2를 추가하세요. 이것이 시퀀스 이메일 내부의 링크가 보이는 형태입니다.이 시리즈만 중단을 클릭
구독자 페이지로 돌아가기: 여전히
active, 여전히 뉴스레터 구독 중, 정확히 하나의 시리즈에서만 제외됨동의 패널에는 단일 시리즈를 떠난 사람들이 표시되고, 완전히 제거된 사람 목록은 (비어 있습니다)
이후 브로드캐스트를 발송해도 그들은 여전히 받습니다. 이것이 전부입니다.
시퀀스 지연은 일(日) 단위이며, 첫 번째 단계는 기본값 0(가입 시 도착)이고 이후 단계는 기본값 1입니다. 즉, 시딩된 시리즈는 지켜보는 동안 완료되지 않으므로, 대시보드에 시계 빨리감기(로컬 전용)가 있습니다: 모든 대기 중인 단계를 현재로 당기고 틱을 실행합니다. 사용하면 2단계가 그 시리즈를 떠난 사람들을 건너뛰는 것을 볼 수 있습니다.
구성 🗂
src/
worker.tsx fetch + scheduled + queue handlers — the whole entry point, 151 lines
core/ domain logic: consent, sending, sequences, segments, rendering
db/ Drizzle schema (24 tables) and the D1 client
web/ server-rendered admin console (Hono + JSX, no frontend framework)
api/ transactional send API, signup forms, media upload, bearer-key auth
mcp/ MCP server — 93 tools, 4 resources, 4 prompts
providers/ EmailProvider port + console and Resend adapters
client/ the only browser JS in the project: the TipTap editor bundle
migrations/ drizzle-kit generated, applied by wrangler
docs/ problem brief, architecture, spec, and a decision log약 16,000줄의 TypeScript. bun run typecheck는 Worker와 브라우저 번들을
각각 검사하며 깨끗합니다.
아키텍처 한눈에 보기 🧱
Cloudflare Workers · D1(SQLite) via Drizzle · 발송 팬아웃용 Queues · 스케줄링용
Cron Triggers · 미디어용 R2 · Hono + JSX 서버 렌더링 관리자 · 인증용 Cloudflare
Access · 플러그 가능한 EmailProvider 포트(console 및 Resend 어댑터).
알아둘 가치가 있는 결정과 그 이유:
모든 messages 행은 단 한 건의 발송이 나가기 전에 구체화됩니다. 브로드캐스트는
전체 수신자 목록을 사전에 해석하고, 의도된 발송마다 행을 기록한 다음에야 큐로
팬아웃합니다. 이로써 브로드캐스트는 크래시 후 재개 가능하고, 큐 재시도에 걸쳐
멱등적이며, 이후 감사가 가능합니다. 발송 시점에 수신자를 지연 해석하는 것은 더
저렴하지만 브로드캐스트 중간의 실패를 복구 불가능한 난장판으로 만듭니다.
동의는 큐에 넣을 때가 아니라 프로바이더 호출 직전에 다시 확인됩니다. 큐는 메시지가 생성된 후 몇 분 뒤에 전달될 수 있으며, 그 사이 누군가 옵트아웃할 수 있습니다. 큐에 넣을 때 확인하면 어쨌든 그들에게 메일을 보내게 됩니다.
큐 동시성은 6으로 고정됩니다. 배치 하나가 프로바이더 요청 하나이므로, 배치 동시성이 곧 요청 속도입니다. 설정하지 않으면 Cloudflare Queues는 250개의 동시 컨슈머로 자동 확장되어 Resend의 10 req/s 제한을 429 오류로 묻어버리고, 3번의 재시도를 모두 소진하며, 완벽히 정상적인 메일을 데드 레터로 만듭니다. 100개씩 6배치는 제한 아래에 머물면서 초당 ~600개의 이메일 여유를 확보합니다.
억제(suppression)는 구독자가 아니라 이메일 주소를 키로 사용합니다.
트랜잭셔널 수신자와 바운스된 주소는 구독자 행이 없는 경우가 많으므로,
subscribers.status 플래그로는 조용히 놓치게 됩니다.
관찰 가능한 모든 것은 로그 줄이 아니라 D1의 행입니다. Workers 로그는 3~7일
후 만료됩니다. 1주 보존 기간의 감사 추적은 감사 추적이 아닙니다. mcp_calls는
거부된 호출을 포함한 모든 에이전트 작업을 기록하고, sync_runs는 모든 Stripe
풀을 기록합니다.
관리 콘솔에는 비밀번호가 없습니다. Cloudflare Access가 엣지에서 신원을
종료하며, src/web/auth.ts는 전달된 JWT를 제대로 검증합니다: 팀의 라이브 JWKS에
대한 서명(격리당 캐시, 알 수 없는 키 ID에서 강제 재조회), alg를 RS256으로
고정, 그리고 audience, issuer, exp, nbf까지. 헤더의 존재는 아무것도 증명하지
않으며 증명으로 취급되지 않습니다. 잘못 구성하면 미들웨어는 실패 시 잠금되어
당신을 포함한 모두를 잠급니다. 그것이 올바른 실패 방향입니다.
이메일 HTML 렌더러는 직접 작성되었습니다(core/render-doc.ts). @tiptap/html은
서버 진입점에 happy-dom이 필요하고 workerd 내에서 실행되지 않기 때문입니다.
결과적으로 더 나은 답이었습니다: 워커는 모든 스타일을 인라인 처리하고(Gmail은
<style>을 제거함) 버튼에 중첩 테이블을 생성합니다(Outlook은 <a>의 패딩을
무시함). 일반 HTML 직렬화로는 불가능한 일입니다.
대안과 기각된 이유를 포함한 전체 결정 기록은
docs/MEMORY.md에 있습니다.
동의 모델 🔐
세 개의 독립적인 스코프. 좁은 선택이 넓은 선택으로 확대되지 않습니다.
스코프 | 저장 위치 | 효과 |
시퀀스 |
| 그 시리즈 하나만 제외. 나머지는 모두 계속. |
브로드캐스트 |
| 뉴스레터에서 제외. 시리즈는 계속 실행. |
글로벌 |
| 모든 것에서 제외. 법적 탈출구. |
오직 명시적인 "모든 것에서 구독 해지", 하드 바운스, 또는 신고만이 글로벌 억제를 기록합니다.
시퀀스 발송은 의도적으로 status = 'unsubscribed'를 무시합니다. 그 플래그는
브로드캐스트 스코프이기 때문입니다: 뉴스레터를 떠난 사람도 요청한 온보딩 시리즈는
계속 받습니다. 트랜잭셔널 메일(영수증, 다운로드)은 마케팅 동의를 완전히 무시하며
죽은 주소나 스팸 신고에 의해서만 차단됩니다. 영수증은 마케팅이 아니며, 구독
해지한 고객도 다운로드는 필요합니다.
에디터 ✍️
블록 기반 리치 텍스트, TipTap v3, 바닐라(React 없음). 시딩된 초안 **"Draft: everything the editor can do"**를 열면 전부 확인할 수 있습니다.
줄에서
/→ 블록 메뉴: 제목, 목록, 체크리스트, 인용, 코드, 표, 토글, 구분선, 이미지, YouTube, CTA 버튼@→ 개인화 필드가 실제 노드로,first_name의 오타가 불가능왼쪽 여백의 핸들을 드래그하여 재정렬; shift-선택으로 여러 블록 선택
아무 곳에나 이미지를 드롭하거나 붙여넣기 → R2에 업로드, URL이 반환되면 삽입
코드 블록은 구문 강조 지원(15개 언어, Ruby, Elixir, TS, SQL 포함)
텍스트 선택 시 버블 메뉴; 버튼 선택 시 버블 메뉴가 URL 및 색상 선택기로 변경
버튼과 병합 태그는 이메일 전용으로 구축된 커스텀 노드입니다. CTA는 중첩
테이블로 렌더링되며 모든 스타일이 인라인 처리됩니다. 병합 태그는 원시
{{first_name}} 텍스트가 아닌 노드입니다. 원시 텍스트의 오타는 "Hi
{{frist_name}}"을 전체 목록에 발송하기 때문입니다.
본문은 body_json에 TipTap JSON으로 저장됩니다. body_md의 레거시 마크다운은
여전히 렌더링되며 에디터에서 여는 순간 변환됩니다. 대량 마이그레이션은 없습니다.
잘못된 대량 마이그레이션이 아카이브를 함께 망칠 수 있기 때문입니다.
클라이언트 번들은 약 226KB gzipped이며 메일을 작성하는 두 화면에서만 로드됩니다. 관리 콘솔의 나머지는 JavaScript 없이 서버 렌더링됩니다.
실제 Chromium을 구동하는 브라우저 스모크 테스트(bun run smoke, 33개 검사)가
있습니다. 확장 옵션 이름 변경은 브라우저에서 조용히 실패하고 본문 필드가 저장되지
않기 때문입니다. 서버 측에서는 그런 것을 잡을 수 없습니다.
Claude Code에서 구동하기 🤖
Worker는 POST /mcp/<secret>에서 MCP 서버를 제공합니다: 93개의 도구로
메일러 전체를 다루므로, 에이전트가 세그먼트를 자르고, 브로드캐스트를 작성하고
발송하며, 시퀀스를 구축하고, 캠페인 성과를 읽고, Stripe를 조정할 수 있습니다.
# 1. a path secret (this is what makes the endpoint exist at all)
openssl rand -hex 24 # → put in .dev.vars as MCP_PATH_SECRET
# 2. an admin-scoped key — POST /seed prints one, or use apikey_create
# 3. point Claude Code at it
claude mcp add --transport http --scope local \
--header "Authorization: Bearer $BIG_MAILER_KEY" \
big-mailer "http://localhost:8787/mcp/$MCP_PATH_SECRET"세 개의 게이트, 가장 저렴한 것부터. 상수 시간 비교로 검증되는 추측 불가능한
경로 비밀(실패 시 403이 아닌 404를 반환합니다. 추측되지 않은 URL은 아무것도
없는 것처럼 보여야 하기 때문입니다), 그 다음 admin 스코프의 베어러 키(트랜잭셔널
send 키는 접근 불가), 그 다음 도구별 가드. 거부된 호출을 포함한 모든 호출이
mcp_calls에 기록됩니다.
되돌릴 수 없는 발송에는 사전 점검이 필요합니다. broadcast_send는
broadcast_preflight의 토큰 없이는 거부합니다: 일회용, 10분 만료, 콘텐츠나
대상의 편집 시 무효화. sequence_activate도 동일합니다. 게다가 프로덕션에서
MCP_ALLOW_SEND는 "false"이므로, MCP는 읽기와 초안 작성은 가능하지만 당신이
의도적으로 켜기 전까지는 메일을 발송할 수 없습니다. 다시 끄는 것이 즉시
차단 스위치입니다.
에이전트는 동의를 건드리기 전에 bigmailer://conventions를 읽어야 합니다.
스코프별 구독 해지는 일반 ESP에 대해 훈련된 어떤 모델도 기대하지 않는 형태이며,
그것을 잘못 이해하는 것이 정확히 이 프로젝트가 피하려는 실패입니다.
Stripe → 캠페인 기여 💳
STRIPE_SECRET_KEY를 설정하세요(제한된 권한, charges/refunds/customers 읽기
전용). UTC 09:17의 일일 크론이 새 청구를 가져와 각각을 구매자의 마지막 기여
터치에, 또는 청구에 metadata.campaign이 있을 때는 그것에 귀속시킵니다. Stripe
청구 ID에 대해 멱등적이므로 재실행과 중복 백필은 무해합니다.
stripe_sync_preview는 드라이런을 수행하고, sales_unattributed는 휴리스틱이
귀속시킬 수 없었던 작업 목록이며, sync_runs_list는 야간 작업이 실제로
실행 중임을 증명합니다.
명령어 ▶️
| 클라이언트 번들 빌드 후 :8787에서 서빙 |
| 변경 시 에디터 번들 재빌드( |
| 에디터 브라우저 스모크 테스트. |
| 로컬 D1에 마이그레이션 적용 |
|
|
| 로컬 데이터베이스에 대한 Drizzle Studio |
| Worker와 브라우저 번들을 각각 타입 검사 |
| 빌드 후 |
실제 발송하기 📮
.dev.vars.example을 .dev.vars로 복사하고, Resend 키를 추가한 다음
EMAIL_PROVIDER=resend로 설정하세요. Resend의 웹훅을 /webhooks/resend로 지정하여 반송(bounce)과
불만 신고(complaint)가 제대로 억제되도록 하세요. 해당 웹훅이 없으면 잘못된 주소는 절대 억제되지 않으며
발신 평판이 조용히 악화됩니다. 이는 전체 도메인의 전달 가능성을 서서히 잃는 방법입니다.
비밀 값은 .dev.vars(gitignore됨) 또는 wrangler secret put에 넣으세요. 절대
wrangler.jsonc나 .dev.vars.example에는 넣지 마세요.
⚠️ 배포 전에
아직 배포되지 않았으며, 여기에서 실제 서비스까지는 준비해야 할 것이 있습니다:
DEV_AUTH_BYPASS=true가 최상위wrangler.jsonc변수에 있어 로컬에서 앱을 실행할 수 있습니다.--env production은 이를 false로 설정합니다. 단순한wrangler deploy는 인증되지 않은 관리자 콘솔을 게시합니다. 이것이bun run deploy가--env production을 하드코딩하는 이유입니다. 이를 우회하지 마세요.database_id는 자리 표시자입니다. 실제 D1 데이터베이스를wrangler d1 create로 만드세요.R2 버킷(
big-mailer-media)과 두 개의 큐 (big-mailer-send,big-mailer-dlq)를 만드세요. 큐는 월 5달러 Workers 유료 플랜이 필요합니다.PUBLIC_URL을 실제 호스트로 설정하세요. 이 값은 추적 링크와 이미지 URL에 전송 시점에 포함되므로, 잘못된 값은 이미 전달된 메일을 영구적으로 잘못된 이메일로 보내게 됩니다. 나중에 고칠 수 없습니다.MCP_PATH_SECRET은 변수가 아닌 실제 비밀 값이어야 합니다 (wrangler secret put). 설정하지 않으면 MCP 엔드포인트가 404를 반환하며, 이것이 안전한 기본값입니다. 의도적으로 켜세요.Cloudflare Access에는 하나의 Allow 앱과 여러 개의 Bypass 앱이 필요합니다. 전체 호스트 이름을 단일 Allow 정책으로 보호하면 추적 픽셀, 가입 폼, 환경설정 센터, 웹훅, MCP도 보호되어, 보낸 모든 이메일의 모든 추적 픽셀이 이미 전달된 메일에 대해 영구적으로 로그인 화면으로 리디렉션됩니다. Access는 가장 구체적인 경로를 먼저 매칭하므로
/t,/f,/p,/api,/webhooks,/mcp각각에 자체 Bypass 앱이 필요합니다. 각각은 자체 인증을 갖고 있거나 설계상 공개입니다.서버 측 테스트 스위트가 없습니다.
bun run smoke는 편집기를 다룹니다.docs/SPEC.md는 번호가 매겨진 테스트 가능한 요구 사항으로 작성되어 실행 가능하도록 구성되어 있습니다.
문서 📚
문제, 대상 사용자, 그리고 명시적으로 범위에서 제외된 것 | |
시스템 설계, 데이터 모델, 발신 파이프라인 | |
번호가 매겨진 동작 요구 사항. 의도된 동작에 대한 참조 | |
의사 결정 기록: 선택한 것, 거부한 것, 그리고 그 이유 |
기여 🤝
버그 보고, 정확성 수정, 이메일 클라이언트 렌더링 수정은 환영합니다.
멀티 테넌시, 드래그 앤 드롭 빌더, 자체 실행 SMTP는 의도적으로 범위에서 제외되었습니다.
PR을 열기 전에 CONTRIBUTING.md를 읽고,
취약점을 발견했다면(이슈가 아닌 비공개로 신고해 주세요) SECURITY.md를 참조하세요.
포크는 적극 권장됩니다. 이 프로젝트는 처음부터 끝까지 읽고 자신만의 것으로 만들 수 있을 만큼 작습니다. 참여는 행동 강령에 따릅니다.
라이선스 📄
MIT © Rob Conery
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseCqualityBmaintenanceMCP server that exposes the complete Libredesk REST API (54 endpoints) as tools, enabling natural language management of conversations, contacts, agents, teams, and more for the open-source customer support desk.54113MIT
- AlicenseAqualityFmaintenanceAn MCP server for the Resend email API, enabling AI assistants to send emails, manage contacts, audiences, and domains through natural language.1844MIT

xmit-mcpofficial
FlicenseNot gradedqualityDmaintenanceRemote MCP server for the Transmit email platform, enabling email sending, contact management, template and campaign operations via natural language.- FlicenseCqualityDmaintenanceComprehensive MCP server for Mailchimp Marketing API v3.0 with over 104 tools and 15+ React UI apps, enabling management of campaigns, audiences, ecommerce, automations, reports, and more via natural language.1001
Related MCP Connectors
GibsonAI MCP server: manage your databases with natural language
Personal assistant MCP server with search, execute, packages, jobs, secrets, and integrations.
Markdown-first MCP server for Notion API with 8 composite tools and 39 actions.
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/robconery/big-mailer'
If you have feedback or need assistance with the MCP directory API, please join our Discord server