Skip to main content
Glama
HalloSouf

postbus-mcp

by HalloSouf

postbus-mcp

Code quality Docker License: MIT

직접 호스팅하는 MCP 서버로, 당신과 주변의 몇몇 사람들이 Claude나 다른 MCP 클라이언트에서 사서함을 사용할 수 있게 해줍니다: 검색, 전체 대화 읽기, 메일 발송.

모든 IMAP/SMTP 제공자와 작동합니다 — Gmail, Outlook, Fastmail, 자체 메일 서버 — 일반 앱 비밀번호만 사용합니다. Google Cloud 프로젝트, OAuth 검증, 테스트 사용자 제한이 없습니다.

단일 인스턴스가 여러 사용자를 지원합니다. 각 사용자는 고유한 API 토큰을 받으며 자신의 사서함만 볼 수 있습니다. 당신이 호스팅하고 토큰도 당신이 나눠줍니다. 공개 가입은 없습니다.

Claude / MCP client
        │  Authorization: Bearer <token>
        ▼
   POST /mcp  ──►  postbus-mcp  ──►  SQLite (users + encrypted app passwords)
                        │
                        ├──►  IMAP  (imapflow)      search, read, threads
                        └──►  SMTP  (nodemailer)    sending

목차


Related MCP server: simple-email-mcp

작동 방식

멀티 테넌트이지만 소규모입니다. SQLite 파일 하나에 테이블 두 개가 있습니다: users(id와 API 토큰의 해시) 및 mail_accounts(각 사용자의 사서함, 앱 비밀번호는 암호화됨). 실행할 별도의 데이터베이스 서비스는 없습니다.

격리는 사후 검사가 아니라 쿼리에 있습니다. 모든 MCP 세션은 bearer 토큰으로 결정되는 정확히 한 명의 사용자에게 속합니다. MCP 서버는 요청마다 해당 사용자를 기준으로 생성되며, 모든 데이터베이스 쿼리는 WHERE에 user_id를 포함합니다. 다른 사람의 별칭은 세션에 아예 존재하지 않습니다.

제공자 인터페이스. 도구 계층은 일반적인 MailProvider와 통신하며 IMAP이나 Gmail에 대해 알지 못합니다. ImapSmtpProvider가 주요 구현이며, 선택적으로 GmailApiProvider를 함께 사용할 수 있습니다. 세 번째 제공자를 추가해도 도구는 변경할 필요가 없습니다 — 제공자 추가를 참조하세요.


빠른 시작

Docker 사용(권장)

git clone https://github.com/HalloSouf/postbus-mcp.git
cd postbus-mcp

cp .env.example .env
openssl rand -hex 32          # put the result in .env as MASTER_KEY

docker compose up -d --build
docker compose exec postbus node dist/cli/add-user.js "Soufiane"

마지막 명령은 API 토큰을 정확히 한 번만 출력합니다. 즉시 저장하세요.

Node로 로컬 실행(22 이상)

npm install
cp .env.example .env
openssl rand -hex 32          # put the result in .env as MASTER_KEY

npm run build
npm run add-user -- "Soufiane"
npm start

서버는 http://localhost:3000/mcp에서 수신 대기합니다. GET /health는 {"status":"ok"}를 반환하므로 가동 상태 확인에 유용합니다.


사용자 및 토큰

토큰은 직접 나눠줍니다. 셀프서비스 가입은 없습니다.

Command

기능

npm run add-user -- "Name"

사용자를 생성하고 토큰을 출력합니다(한 번만)

npm run list-users

사용자, 사서함 수, 상태를 표시합니다

npm run rotate-token -- <id>

새 토큰 발급; 이전 토큰은 즉시 사용 중지됩니다

npm run remove-user -- <id>

사용자와 해당 사용자의 모든 사서함을 삭제합니다

Docker에서는 동일한 스크립트를 node dist/cli/<script>.js로 실행합니다:

docker compose exec postbus node dist/cli/list-users.js
docker compose exec postbus node dist/cli/rotate-token.js WvDnhafdM5yQ

각 토큰의 SHA-256 해시만 저장되므로 분실한 토큰은 조회할 수 없습니다. 대신 토큰을 교체하세요.


클라이언트 연결

Claude Desktop

Claude Desktop은 stdio를 사용하므로 중간에 mcp-remote를 둡니다. claude_desktop_config.json에:

{
  "mcpServers": {
    "postbus": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://mcp.example.com/mcp",
        "--header",
        "Authorization: Bearer pb_YOUR_TOKEN_HERE"
      ]
    }
  }
}

이 파일은 macOS에서 ~/Library/Application Support/Claude/claude_desktop_config.json, Windows에서 %APPDATA%\Claude\claude_desktop_config.json에 있습니다. 편집 후 Claude Desktop을 다시 시작하세요.

Claude Code

claude mcp add --transport http postbus https://mcp.example.com/mcp \
  --header "Authorization: Bearer pb_YOUR_TOKEN_HERE"

기타 클라이언트

Streamable HTTP를 지원하는 모든 클라이언트가 작동합니다: 엔드포인트 POST /mcp, 토큰은 Authorization: Bearer <token>으로 전달합니다. 서버는 무상태(stateless)로 실행됩니다 — 세션 ID도, 서버 측 스트림도 없습니다 — 따라서 GET /mcp는 의도적으로 405를 반환합니다.


사서함 연결

이 작업은 대화에서 자체 토큰으로 수행합니다. 터미널이 필요 없습니다:

내 Gmail을 "personal"로 연결해 줘, 주소 souf@gmail.com, 앱 비밀번호 abcd efgh ijkl mnop

그러면 Claude가 add_mail_account를 호출합니다. 연결이 먼저 테스트됩니다(IMAP과 SMTP 모두). 둘 다 작동할 때까지 아무것도 저장되지 않습니다.

앱 비밀번호 만들기

제공자

위치

참고

Gmail / Workspace

https://myaccount.google.com/apppasswords

계정에 2FA 필요

Outlook / Microsoft 365

https://account.microsoft.com/security

2FA 필요, 관리자가 IMAP을 차단할 수 있음

Fastmail

설정 → 개인정보 및 보안 → 앱 비밀번호

"Mail (IMAP/SMTP)" 선택

iCloud

https://account.apple.com → 앱 전용 비밀번호

2FA 필요

자체 서버

n/a

메일 비밀번호 또는 전용 계정

제공자가 앱 비밀번호를 제공하는 경우 일반 비밀번호를 사용하지 마세요.

호스트 및 포트

알려진 제공자의 경우 postbus-mcp가 이 값을 자동으로 입력합니다 — 별칭, 이메일, 앱 비밀번호만 제공하면 됩니다:

Gmail, Google Workspace, Outlook, Hotmail, Microsoft 365, Fastmail, iCloud, Yahoo, Zoho, Proton(Bridge 사용).

그 외의 경우 직접 전달하세요:

imap_host: imap.yourdomain.com    imap_port: 993   (TLS)
smtp_host: smtp.yourdomain.com    smtp_port: 465   (TLS) or 587 (STARTTLS)

993 및 465 포트는 첫 바이트부터 TLS를 사용합니다. 다른 포트에서는 서버가 지원하는 경우 STARTTLS가 사용됩니다. 이 가정이 서버에 맞지 않으면 imap_secure 또는 smtp_secure를 명시적으로 전달하세요.


사용 가능한 도구

도구

기능

list_accounts

별칭과 이메일 주소로 사서함을 나열합니다

add_mail_account

앱 비밀번호로 IMAP/SMTP 사서함을 연결합니다(연결을 먼저 테스트)

remove_mail_account

사서함 연결을 해제하고 저장된 앱 비밀번호를 삭제합니다

search_emails

Gmail 스타일 구문으로 검색합니다. 메시지마다 id와 threadId를 반환합니다

get_message

단일 메시지의 전체 콘텐츠: 헤더, 본문, 첨부파일 메타데이터

get_thread

대화의 모든 메시지를 오래된 순서대로 반환합니다

send_email

새 메시지를 즉시 전송합니다(cc, bcc, reply-to, html)

모든 도구는 토큰 뒤에 있는 사용자의 사서함에만 접근합니다.


검색 구문

search_emails는 Gmail 스타일 구문을 사용합니다. Gmail 사서함의 경우 쿼리가 변경 없이 Gmail로 전달되므로(X-GM-RAW 사용), Gmail 검색창에서 작동하는 모든 것이 여기서도 작동합니다. 기타 IMAP 서버에서는 쿼리가 변환됩니다:

검색어

Gmail

기타 IMAP

from:, to:, cc:, bcc:, subject:

✅

✅

is:unread, is:read, is:starred, is:answered

✅

✅

newer_than:7d, older_than:2w (d/w/m/y)

✅

✅

after:2026-01-01, before:2026/03/01

✅

✅

larger:5M, smaller:100k

✅

✅

has:attachment

✅

✅ (이후 필터링됨)

in:inbox, in:sent, in:archive, in:all, in:trash

✅

✅ (SPECIAL-USE 사용)

-from:someone (제외)

✅

✅

"exact phrase" 및 자유 단어

✅

⚠️ 단일 결합 텍스트 검색어

label:, filename:, category:

✅

❌ 무시됨

예시:

from:boss@company.com is:unread newer_than:7d
subject:"march invoice" has:attachment
in:sent to:client@example.com older_than:1m

빈 검색어는 받은편지함에서 가장 최근 메시지를 반환합니다.


스레딩

모든 search_emails 결과에는 threadId가 포함되며, get_thread는 이를 사용해 전체 대화를 가져옵니다 — 메시지별로 발신자, 제목, 날짜, 본문이 포함된 시간순 대화입니다.

서버가 지원하는 기능에 따라 두 가지 방식이 있습니다:

  • Gmail(X-GM-THRID) 및 RFC 8474 서버(OBJECTID) 는 안정적인 스레드 ID를 자체적으로 제공합니다. 이를 직접 사용하며, threadId는 srv:1829384756 형태입니다.

  • 그 외 모든 IMAP 서버에는 스레드 개념이 없습니다. 이 경우 표준 Message-ID, In-Reply-To, References 헤더에서 대화를 재구성합니다. 해당 체인의 첫 번째 ID가 스레드의 루트입니다. 이런 threadId 값은 ref:로 시작합니다.

가져올 때 서버에 "all mail" 폴더가 있으면 그 폴더를, 없으면 Inbox, Sent, Archive를 살펴봅니다. 그래서 직접 보낸 답장도 대화에 포함됩니다.


Traefik 뒤에 배포

이 저장소의 docker-compose.yml은 실제 작동하는 예시입니다. 핵심 내용은 다음과 같습니다:

services:
  postbus:
    build: .
    restart: unless-stopped
    environment:
      MASTER_KEY: ${MASTER_KEY:?set MASTER_KEY in .env}
      DATABASE_PATH: /data/postbus.db
      TRUST_PROXY: "true"
    volumes:
      - postbus-data:/data
    networks: [proxy]
    labels:
      traefik.enable: "true"
      traefik.docker.network: proxy
      traefik.http.routers.postbus.rule: Host(`${PUBLIC_HOST:-mcp.example.com}`)
      traefik.http.routers.postbus.entrypoints: websecure
      traefik.http.routers.postbus.tls.certresolver: letsencrypt
      traefik.http.services.postbus.loadbalancer.server.port: "3000"

주의할 점:

  • .env의 PUBLIC_HOST를 자신의 호스트 이름으로 설정하세요. 도메인이 나타나는 유일한 곳이므로 compose 파일 자체는 수정할 필요가 없습니다.

  • proxy 네트워크가 존재해야 하며(docker network create proxy), Traefik이 해당 네트워크에 연결되어 있어야 합니다.

  • 컨테이너는 자체 포트를 게시하지 않습니다. Traefik만 접근할 수 있습니다.

  • TRUST_PROXY=true는 Express가 X-Forwarded-* 헤더를 신뢰하도록 합니다.

  • TLS는 Traefik에서 종료하세요. 토큰은 bearer 자격 증명으로 전달되며, HTTPS가 없으면 평문으로 노출됩니다.

  • postbus-data 볼륨에는 모든 암호화된 앱 비밀번호가 담긴 데이터베이스가 있습니다. 별도로 보관된 MASTER_KEY와 함께 백업하세요.


보안

MASTER_KEY. 앱 비밀번호와 리프레시 토큰은 각각 고유한 IV와 함께 AES-256-GCM으로 저장됩니다. 서버는 키 없이는 시작되지 않습니다. 키를 분실하면 모든 사용자가 사서함을 다시 연결해야 하므로, 데이터베이스 백업과 분리해서 보관하세요.

토큰. SHA-256 해시만 저장됩니다. 신뢰할 수 있는 채널로 공유하고, 의심스러우면 교체하세요(npm run rotate-token).

격리. mail_accounts에 대한 모든 쿼리는 user_id로 필터링되며, MCP 서버는 요청마다 단일 사용자에 맞춰 생성됩니다. 따라서 사용자를 혼동할 수 있는 세션 저장소가 없습니다.

이 프로젝트가 아닌 것. 속도 제한, 감사 로그, 세분화된 권한이 없습니다. TLS 뒤에서 아는 소수의 사람들을 위해 만들어졌습니다. 알 수 없는 대중에게 공개하지 마세요.


제공자 추가

도구 계층은 항상 src/types.ts의 MailProvider와만 통신합니다:

interface MailProvider<A extends MailAccount = MailAccount> {
  readonly id: ProviderId;
  verify(account: A): Promise<void>;
  search(account: A, query: string, maxResults: number): Promise<MessageSummary[]>;
  getMessage(account: A, messageId: string): Promise<MessageDetail>;
  getThread(account: A, threadId: string): Promise<MessageDetail[]>;
  send(
    account: A,
    to: string,
    subject: string,
    body: string,
    options?: SendOptions,
  ): Promise<string>;
}

제공자는 자격 증명이 복호화된 완전히 해석된 계정을 받습니다. 별칭 조회는 도구 계층에서 수행되므로 제공자는 세션 사용자 밖으로 접근할 수 없습니다.

제공자를 추가하려면:

  1. src/types.ts에서 ProviderId와 MailAccount 유니온을 확장합니다.

  2. 인터페이스를 구현하는 클래스와 함께 src/providers/<name>/provider.ts를 작성합니다.

  3. src/providers/registry.ts의 맵에 한 줄을 추가합니다.

  4. 해당 유형의 계정이 데이터베이스에 접근할 수 있는지 확인합니다: src/db/accounts.ts의 save<Name>Account()(비밀 값은 encryptSecret을 통과)와 계정 연결 수단 — add_mail_account 옆의 추가 도구 또는 CLI 스크립트가 필요합니다.

기존 도구(search_emails, get_message, get_thread, send_email)는 변경할 필요가 없습니다. CONTRIBUTING.md도 참조하세요.


선택 사항: IMAP 대신 API를 통한 Gmail

이 저장소에는 IMAP/SMTP 대신 Gmail API를 통해 Gmail에 접근하는 두 번째 프로바이더가 포함되어 있습니다. 거의 사용할 일이 없을 것입니다. 앱 비밀번호를 사용한 IMAP이 훨씬 간단하게 같은 작업을 수행하기 때문입니다. 이는 조직에서 IMAP은 차단하지만 API는 허용하는 경우에만 유용합니다.

  1. https://console.cloud.google.com에서 프로젝트를 만드세요.

  2. API 및 서비스 → 라이브러리 → "Gmail API" 검색 → 사용 설정.

  3. API 및 서비스 → OAuth 동의 화면 → 외부 선택 → 이름과 지원 이메일을 입력하세요.

  4. 연결하려는 이메일 주소를 테스트 사용자에 추가하세요.

  5. 사용자 인증 정보 → 사용자 인증 정보 만들기 → OAuth 클라이언트 ID → 데스크톱 앱 선택.

  6. 클라이언트 ID와 비밀번호를 .env에 넣으세요:

    GOOGLE_CLIENT_ID=xxxxxxxxxxxx.apps.googleusercontent.com
    GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxxxxx
    OAUTH_CALLBACK_PORT=53682
  7. 사서함을 연결하세요. Google이 콜백을 localhost로 보내기 때문에 이 작업은 관리자 컴퓨터에서 실행됩니다:

    npm run list-users                       # look up the user id
    npm run link-gmail -- <user-id> work

사용되는 스코프: gmail.readonly, gmail.send, gmail.compose, gmail.labels.

참고: OAuth 동의 화면이 테스트로 설정되어 있는 동안에는 리프레시 토큰이 7일 후 만료되어 다시 연결해야 합니다. 이는 동의 화면이 프로덕션으로 전환되어야만 멈추며, 이 스코프에는 Google 검증이 필요합니다. 이것이 바로 앱 비밀번호를 사용한 IMAP이 기본 경로인 이유입니다.


개발

npm install
npm run dev          # server with hot reload (tsx watch)
npm test             # unit tests (vitest)
npm run typecheck    # src + tests
npm run format       # prettier across the repo
npm run build        # into dist/

tests/의 테스트는 0.5초 안에 실행되며 프로세스 외부의 어떤 것도 건드리지 않습니다. SQLite는 메모리에서 실행되고 어떤 연결도 머신 밖으로 나가지 않습니다. 이 테스트는 조용히 잘못될 수 있는 로직을 다룹니다 — 검색 쿼리 변환, 메시지 및 스레드 ID 인코딩, MIME 파싱 및 작성, 암호화된 저장소, 사용자 간 분리, 베어러 미들웨어.

이 테스트가 다루지 않는 것은 실제 메일 서버와의 통신입니다. 그러려면 GreenMail을 로컬에서 실행하세요:

docker run -d --rm --name greenmail -p 3143:3143 -p 3025:3025 \
  -e GREENMAIL_OPTS='-Dgreenmail.setup.test.imap -Dgreenmail.setup.test.smtp -Dgreenmail.users=souf:secret@postbus.test -Dgreenmail.hostname=0.0.0.0' \
  greenmail/standalone:2.1.0

그런 다음 imap_host: 127.0.0.1, imap_port: 3143, smtp_host: 127.0.0.1, smtp_port: 3025, username: souf, app_password: secret 설정으로 사서함을 연결하세요.

GreenMail은 Gmail 확장 기능을 지원하지 않습니다. X-GM-RAW와 X-GM-THRID를 사용하는 코드 분기는 실제 Gmail 사서함에서만 테스트할 수 있습니다.

GitHub Actions는 모든 push와 pull request에 대해 동일한 검사를 실행합니다: 포맷팅, 타입 검사, 프로덕션 의존성에 대한 npm audit, 테스트, 그리고 컨테이너를 시작해서 /health가 응답하고 /mcp가 토큰 없이 401을 반환하는지 확인하는 docker 빌드입니다. CI와 컨테이너는 모두 현재 LTS인 Node 24를 실행합니다.

프로젝트 구조

src/
├── index.ts              startup: check MASTER_KEY, open the db, listen
├── config.ts             environment configuration
├── crypto.ts             AES-256-GCM for secrets, hashing for tokens
├── types.ts              MailProvider plus every shared type
├── db/                   SQLite: migrations, users, mail_accounts
├── http/                 Express app, bearer auth, MCP transport per request
├── providers/
│   ├── registry.ts       account -> provider
│   ├── imap/             IMAP/SMTP: connections, search, threading, sending
│   └── gmail/            optional Gmail API provider (OAuth)
├── tools/                the MCP tools (they know no provider)
└── cli/                  admin scripts: users and tokens

tests/                    unit tests (vitest), mirroring the layout of src/

라이선스

MIT — LICENSE를 참조하세요.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables reading and sending emails via IMAP and SMTP through the MCP protocol. Supports multiple email accounts and configuration via UI or environment variables.
    BSD 3-Clause
  • A
    license
    B
    quality
    B
    maintenance
    Enables users to manage email accounts via IMAP/SMTP, including reading, searching, sending emails with attachments and calendar invites, all through natural language interactions with MCP-compatible clients.
    1
    40 PyPI
    4
    MIT
  • A
    license
    B
    quality
    A
    maintenance
    Connects MCP clients to any IMAP/SMTP email account, enabling email search, reading, sending, replying, forwarding, flagging, moving, and folder management via natural language.
    17
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    Enables email integration through MCP, including sending, receiving, searching, replying to, and forwarding emails, plus folder management and multi-account support with encrypted credential storage.
    40
    1,152 npm
    MIT