Skip to main content
Glama
tqq0531

QQ Mail Reader MCP

by tqq0531

QQ Mail Reader MCP

English | 简体中文

Node.js MCP License: MIT Tests

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.

Related MCP server: Mail Agent MCP

Architecture

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:

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

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

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:

npm run test:manual

See TESTING.md for detailed steps.

Automated tests

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:

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
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 for the detailed milestones and known limitations.

Security

Never commit .env.local, data/, QQ Mail authorization codes, or API keys. See SECURITY.md when reporting a vulnerability, and do not paste credentials into public issues.

License

MIT

Available Tools

5 tools
qq_mail_connection_status检查 QQ 邮箱连接配置A
Read-onlyIdempotent

检查当前连接是否已配置,只返回邮箱地址和配置状态,绝不返回授权码。

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, and openWorldHint=false. The description adds that it only returns the email address and configuration status, never the authorization code, which is a useful disclosure about output sensitivity.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, front-loaded sentence that states the core action, the returned data, and an explicit exclusion without any filler. Every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the simple read-only nature, the empty input schema, and the rich annotations, the description covers what the tool does and what data it returns. It could optionally clarify what 'configuration status' means (e.g., configured/not configured), but it is adequate.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so the baseline for parameter semantics is 4. There are no parameter details to document, and the description appropriately does not discuss inputs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool checks whether the current connection is configured, using a specific verb '检查' (check) and resource '连接配置' (connection configuration). It distinguishes from sibling tools like qq_mail_read and qq_mail_search, which perform mail operations rather than status checks.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit when-to-use or when-not-to-use guidance is provided, nor are any alternatives mentioned. The description only states what the tool does, not when to invoke it versus the sibling mail tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

qq_mail_list_unread列出 QQ 邮箱未读邮件A
Read-onlyIdempotent

以只读方式列出 QQ 邮箱的未读邮件摘要,不会将邮件标记为已读。返回 mailbox 和 uid,可用于读取指定邮件。

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo最多返回的邮件数量
mailboxNo邮箱文件夹,默认 INBOXINBOX

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint and destructiveHint, but the description adds a genuinely useful behavioral detail beyond them: it will not mark messages as read, which is the key side-effect concern for a mail listing tool. It omits pagination and rate-limit behavior, keeping it below a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single dense sentence that front-loads the read-only guarantee and then the return value; every clause carries information and there is no padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description correctly compensates by naming the returned fields (mailbox and uid) and their downstream use. The main remaining gap is any statement about result limits or paging, though limit is covered by the schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both limit and mailbox are already documented in the schema. The description adds no syntax, format or defaulting information beyond that, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource with scope: '以只读方式列出 QQ 邮箱的未读邮件摘要' (list unread QQ mail summaries read-only). The 'unread' qualifier naturally separates it from qq_mail_read and qq_mail_search, though no sibling is named explicitly, so it falls just short of the top mark.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives a clear usage context and a workflow pointer: the returned mailbox and uid '可用于读取指定邮件', implicitly routing the agent to qq_mail_read. It does not, however, state when to prefer qq_mail_search or any exclusions, so it stops at clear context without explicit routing rules.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

qq_mail_profile查看当前 QQ 邮箱连接A
Read-onlyIdempotent

返回当前 OAuth 连接对应的 QQ 邮箱身份,帮助区分多个邮箱连接。不返回授权码。

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare this is a closed-world, read-only, idempotent, non-destructive call, so the bar is low. The description adds a genuine boundary beyond them: it discloses that no authorization code is returned, which prevents an agent from treating this as a credential-fetching tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, front-loaded with the return subject and followed by the intent and the exclusion. No filler, and the negative constraint is placed where it is most likely to be read.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the burden of signaling what comes back, and it does so at a high level ('identity', explicitly not the auth code). A parameterless read tool with full annotation coverage does not need more, though the exact identity fields remain unspecified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is nothing for the description to disambiguate and the schema is fully self-sufficient. Baseline of 4 applies for a parameterless tool.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb and resource: it returns the QQ Mail identity bound to the current OAuth connection, and adds the intent of distinguishing between multiple mailbox connections. It is clear but never names the nearest sibling (qq_mail_connection_status), so an agent must infer the boundary itself.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The clause '帮助区分多个邮箱连接' implies the usage scenario (multiple connections present, identity disambiguation needed) but gives no explicit when-not-to-use rule and names no alternative among qq_mail_connection_status or the read/search siblings. Usage is inferable but not stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

qq_mail_read读取一封 QQ 邮件A
Read-onlyIdempotent

使用列表或搜索结果中的 mailbox 和 uid 读取一封邮件的正文与附件元数据,不会下载附件或标记为已读。

ParametersJSON Schema
NameRequiredDescriptionDefault
uidYes邮件 UID,必须来自列表或搜索结果
mailboxNoINBOX

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already declare the safety profile (readOnlyHint, idempotentHint, destructiveHint=false), and the description adds the genuinely useful behavioral fact that it will not download attachments or mark the message as read — a common ambiguity in mail clients. It stops short of describing error or pagination behavior, but the added context is real.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence that leads with the action, then the data source, then the non-effects. No filler and nothing repeated from the title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description correctly summarizes the return value (body plus attachment metadata) and the absence of side effects. A 2-parameter read tool with annotations is well covered, missing only failure/empty-result behavior.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is only 50%: mailbox has a default but no description, and uid is already documented as needing to come from list/search results. The description compensates partly by asserting both mailbox and uid originate from list or search results, but adds no format, default, or casing details beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource — reading one QQ mail's body and attachment metadata — and scopes it to a single message, which cleanly separates it from qq_mail_list_unread and qq_mail_search that return collections. An agent can pick this tool over its siblings without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states that mailbox and uid must come from a list or search result, which effectively tells the agent this is a follow-up call to qq_mail_list_unread or qq_mail_search. That is clear usage context, though there is no explicit statement of when not to use it or what happens if a stale uid is supplied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 5 tool updatesv0.2.0
    • First observedqq_mail_connection_status
    • First observedqq_mail_list_unread
    • First observedqq_mail_profile
    • First observedqq_mail_read
    • First observedqq_mail_search

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

Related MCP Connectors

  • Read-only IMAP email for your AI agent, scoped to the mailboxes you choose, with built-in progress.

  • Your mailboxes in ChatGPT and Claude: Gmail, iCloud, Fastmail, any IMAP. Passwords stay yours.

  • Connect any mailbox to Claude, ChatGPT & AI: read, send, reply, schedule & search emails.

  • Your agent needs a mailbox of its own — to receive, thread, draft and send, with attachments, without borrowing your personal inbox or your company's SMTP. **What you can ask for** • "Create an inbox for this agent and tell me its address." • "Read the new messages in this thread and draft a reply." • "Send this message with the attachment and wait for the response." • "Search this inbox for everything from that domain." • "Show delivery metrics and the events on this inbox." **How to use it** Point any MCP client at https://mcp.aisa.one/mail/mcp and sign in with OAuth — there is no key to create or paste. 49 tools: create and delete inboxes, list and read messages, raw message bodies, attachments, threads, drafts and draft attachments, send and reply, message search, inbox events, metrics, and list entries — reads and writes. **Why this rather than the source** A real inbox an agent owns, rather than an SMTP credential it borrows from a human. **It is also a door to the rest** The same login reaches 26 sources and 580+ operations. Find the contact elsewhere in the catalogue, then write to them from here — without adding a second server. **What it costs** Finding and inspecting an operation is free. Running one is billed per call at API prices, with no seat and no monthly minimum, and every call takes max_price_usd so an agent cannot overspend by accident. **Where else it reaches** https://mcp.aisa.one/sales/mcp finds the person to write to.

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables LLMs to read, search, and manage emails via IMAP with secure, read-only access to email accounts.
    6
    -
  • A
    license
    B
    quality
    B
    maintenance
    Enables AI agents to securely interact with Gmail and QQ Mail, including IMAP search/read/organization, attachments, and preview-confirmed sending.
    41
    2
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI clients to read, search, manage, and send QQ mailbox emails via IMAP/SMTP, including attachments, drafts, replies, and structured message context for models.
    53 npm
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Connects an IMAP mailbox over OAuth 2.1 so an AI assistant can list and search accounts, folders, messages, attachments, and contacts. Mailboxes stay read-only by default, with optional opt-in write tools for flagging, moving, drafting, and approval-gated sending.
    MIT