Skip to main content
Glama
tqq0531

QQ Mail Reader MCP

by tqq0531
README.md
# QQ Mail Reader MCP

**English** | [简体中文](./README.zh-CN.md)

[![Node.js](https://img.shields.io/badge/Node.js-%E2%89%A520-339933?logo=nodedotjs&logoColor=white)](https://nodejs.org/)
[![MCP](https://img.shields.io/badge/Model%20Context%20Protocol-1.20-blue)](https://modelcontextprotocol.io/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
[![Tests](https://github.com/tqq0531/qq-mail-reader-mcp/actions/workflows/test.yml/badge.svg)](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

A3.8/5.0

Scored across 5 tools

Disambiguation4/5

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.

Naming Consistency4/5

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.

Tool Count5/5

Five tools is a well-scoped set for a focused read-only mail reader, with each tool earning its place without redundancy bloat.

Completeness3/5

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.

Maintenance

ActivityMaintained
ResponsivenessNo issues