QQ Mail Reader MCP
# QQ Mail Reader MCP
**English** | [简体中文](./README.zh-CN.md)
[](https://nodejs.org/)
[](https://modelcontextprotocol.io/)
[](./LICENSE)
[](https://github.com/tqq0531/qq-mail-reader-mcp/actions/workflows/test.yml)
A read-only QQ Mail MCP server for ChatGPT and Codex. It supports both a local single-user mode and a multi-user HTTP mode with OAuth 2.1 and PKCE. Every mail operation uses a read-only QQ Mail IMAP connection.
> Portfolio project: a security-first QQ Mail integration built with Node.js, Model Context Protocol, OAuth 2.1/PKCE, and IMAP.
## Highlights
- **Least privilege by design:** exposes only five tools for identity, connection status, unread messages, search, and message reading. It cannot send, delete, move, or mark mail as read.
- **Complete authorization flow:** implements OAuth discovery, Dynamic Client Registration, Authorization Code with PKCE S256, token rotation, revocation, and UserInfo.
- **Credential protection:** local mode uses macOS Keychain; HTTP mode encrypts QQ IMAP authorization codes with AES-256-GCM, while OAuth tokens are stored only as SHA-256 hashes.
- **Prompt-injection boundary:** email bodies are explicitly treated as untrusted external content, and the MCP server instructions prohibit executing instructions found in messages.
- **Two transports:** stdio for local Codex integration and Streamable HTTP for ChatGPT or other MCP clients.
- **Verifiable behavior:** includes six automated tests plus a real QQ Mail end-to-end test script.
## Architecture
```mermaid
flowchart LR
A[ChatGPT / Codex] -->|MCP tools| B[QQ Mail Reader MCP]
B --> C{Runtime mode}
C -->|Local stdio| D[macOS Keychain]
C -->|HTTP + OAuth 2.1| E[Encrypted OAuth Store]
D --> F[QQ Mail IMAP<br/>imap.qq.com:993]
E --> F
B -->|Read-only| F
```
HTTP mode data flow:
```text
MCP Client -> OAuth discovery / DCR / PKCE -> Bearer token -> /mcp
|
v
per-user encrypted credential
|
v
QQ Mail IMAP
```
## MCP tools
| Tool | Purpose | Changes mailbox state |
| --- | --- | --- |
| `qq_mail_profile` | Returns the identity of the connected mailbox | No |
| `qq_mail_connection_status` | Checks configuration without returning credentials | No |
| `qq_mail_list_unread` | Lists unread message summaries | No |
| `qq_mail_search` | Searches by text, sender, subject, date, or read status | No |
| `qq_mail_read` | Reads a message body and attachment metadata by mailbox + UID | No |
Every tool declares `readOnlyHint: true`, `destructiveHint: false`, and `idempotentHint: true`.
## Quick start
Requirements: Node.js 20+ and a QQ Mail account with IMAP enabled. Use the dedicated **IMAP authorization code**, not the QQ account password. Never send the authorization code through chat.
### Local Codex / MCP client
```bash
git clone https://github.com/tqq0531/qq-mail-reader-mcp.git
cd qq-mail-reader-mcp
npm ci
npm run setup
npm start
```
`npm run setup` stores the email address and IMAP authorization code in macOS Keychain.
### HTTP + OAuth development mode
```bash
npm ci
npm run dev:public
```
The first run creates a local-only `.env.local`. Test data is stored in the Git-ignored `data/` directory, and QQ IMAP authorization codes are encrypted with AES-256-GCM.
Run the real end-to-end flow in another terminal:
```bash
npm run test:manual
```
See [TESTING.md](./TESTING.md) for detailed steps.
## Automated tests
```bash
npm ci
npm test
```
Current coverage includes tool discovery and read-only annotations, encrypted persistence, OAuth discovery, DCR, PKCE, authorization-code exchange, UserInfo, the HTTP 401 challenge, and authenticated MCP calls. Automated tests do not access a real mailbox.
## Production deployment
Required container environment variables:
```env
PUBLIC_BASE_URL=https://your-domain.example
MCP_ENCRYPTION_KEY=<32-random-bytes-in-base64>
OAUTH_DB_PATH=/data/oauth-store.json
HOST=0.0.0.0
PORT=8787
```
```bash
docker build -t qq-mail-reader-mcp .
docker run --rm -p 8787:8787 --env-file .env.local qq-mail-reader-mcp
```
The public MCP endpoint is `https://your-domain.example/mcp`. The deployment environment must allow outbound TCP connections to `imap.qq.com:993`.
The current JSON persistence layer is intended for single-instance validation. Before a multi-instance production rollout, migrate OAuth clients, accounts, and tokens to PostgreSQL or an equivalent managed database while keeping application-layer encryption for QQ authorization codes.
## Current status
- ✅ Local stdio MCP and macOS Keychain credential loading
- ✅ Five read-only QQ Mail tools
- ✅ Multi-user OAuth 2.1 / DCR / PKCE flow
- ✅ Real QQ IMAP end-to-end validation
- ✅ Automated tests: 6/6 passing
- ✅ Dockerfile and Secure MCP Tunnel preflight
- 🚧 Live ChatGPT Secure MCP Tunnel connection and plugin installation
- ⏳ PostgreSQL persistence, audit logging, production legal pages, and marketplace submission
See [STATUS.md](./STATUS.md) for the detailed milestones and known limitations.
## Security
Never commit `.env.local`, `data/`, QQ Mail authorization codes, or API keys. See [SECURITY.md](./SECURITY.md) when reporting a vulnerability, and do not paste credentials into public issues.
## License
[MIT](./LICENSE)
TDQS
Scored across 5 tools
Most tools have distinct purposes (read, list unread, search, profile, connection status), but qq_mail_profile and qq_mail_connection_status both essentially report identity/connection info and could be confused, and qq_mail_list_unread overlaps with qq_mail_search which also filters by unread state.
All tools share the consistent qq_mail_ prefix, but suffixes mix verb styles (read, list_unread, search) with noun phrases (profile, connection_status), a minor deviation from a pure verb_noun pattern.
Five tools is a well-scoped set for a focused read-only mail reader, with each tool earning its place without redundancy bloat.
As a read-only reader the core read/list/search flows are covered, but there is no way to list mailboxes/folders or list all (read) mail beyond unread, leaving notable gaps in the browse surface.