personal-mail-mcp
A local MCP server that connects Codex to Microsoft Graph and Gmail for reading and managing personal mail and calendar accounts.
README
personal-mail-mcp
Local MCP server for Codex access to personal mail and calendar accounts.
It connects Codex to Microsoft Graph and Gmail so a local assistant can review inbox mail, scan for appointment confirmations, create or update calendar entries, find unread mail that may have been missed outside the Inbox, and build safe archive plans for low-value messages. The project also includes Codex skills for recurring workflows such as appointment harvesting, inbox triage, missed-mail review, and full mail review.
This project is provided as-is. It works for my own setup, but it has not been tested broadly across other accounts, tenants, mail providers, or Outlook/Gmail configurations.
Current target:
- GoDaddy-hosted Exchange mailboxes and calendar through Microsoft Graph.
- Gmail through Google APIs.
- Read-only audit/plan tools plus explicit archive and calendar-write tools.
No OAuth secrets or token caches should be committed. Copy
config/accounts.example.toml to config/accounts.toml for local settings.
Copy config/auth.example.toml to config/auth.toml for OAuth app settings.
Keep local config files private to your user account.
Recommended permissions for local-only files:
chmod 700 .private .tokens
chmod 600 config/accounts.toml config/auth.toml config/mail_rules.local.toml
Local project setup
Create and install the local environment:
cd <repo-path>
python3 -m venv .venv
.venv/bin/python -m pip install -e '.[providers]'
Codex config
Add the MCP server to ~/.codex/config.toml:
[mcp_servers.personal_mail]
type = "stdio"
command = "<repo-path>/.venv/bin/python"
args = ["-m", "personal_mail_mcp.server"]
startup_timeout_sec = 30
Restart Codex after changing the config so the MCP server appears in the active tool list.
Account config
Create config/accounts.toml:
[[accounts]]
id = "exchange_primary"
provider = "microsoft"
email = "primary@example.com"
calendar = true
[[accounts]]
id = "exchange_secondary"
provider = "microsoft"
email = "secondary@example.com"
calendar = false
[[accounts]]
id = "google_primary"
provider = "google"
email = "public-example@gmail.com"
calendar = false
This file is ignored by git.
Microsoft app registration
The GoDaddy accounts are Exchange Online accounts reachable through Microsoft Graph. No GoDaddy-specific API is needed.
-
Open the Microsoft Entra admin center:
https://entra.microsoft.com/ -
Go to:
Entra ID > App registrations > New registrationIf the left navigation is different, search for
App registrationsin the portal search box. -
Register the app:
Name: personal-mail-mcp Supported account types: Single tenant only - your Microsoft 365 tenant -
On the app registration Overview page, copy:
Application (client) ID Directory (tenant) ID -
Under Authentication, add a redirect URL for a native/local app:
http://localhostThis appears under Mobile and desktop applications / Redirect URLs in the current portal UI.
-
Under Authentication settings, enable public/native client flows. The portal wording may be one of:
Allow public client flows Enable the following mobile and desktop flows Treat application as a public clientSet it to
Yesand save. -
Under API permissions, add Microsoft Graph delegated permissions:
Mail.Read Mail.ReadWrite Calendars.ReadWrite offline_access
Create config/auth.toml with the copied IDs:
[microsoft]
client_id = "APPLICATION_CLIENT_ID_FROM_ENTRA"
tenant = "DIRECTORY_TENANT_ID_FROM_ENTRA"
[google]
client_secrets_file = ".private/google-oauth-client.json"
Do not put OpenAI, ChatGPT, Codex, or GitHub tokens in this file. This file is ignored by git.
Connect Exchange Online accounts
cd <repo-path>
.venv/bin/python -m personal_mail_mcp.cli status
PYTHONUNBUFFERED=1 .venv/bin/python -m personal_mail_mcp.cli connect exchange_primary
PYTHONUNBUFFERED=1 .venv/bin/python -m personal_mail_mcp.cli connect exchange_secondary
Each connect command prints a Microsoft device-code URL and code. Open the URL, enter the code, and sign in with the matching account:
exchange_primary -> primary Exchange Online mailbox
exchange_secondary -> secondary Exchange Online mailbox
Successful connections write local token cache files under .tokens/, which is
ignored by git.
Verify:
.venv/bin/python -m personal_mail_mcp.cli status
The Microsoft accounts should show:
token_cached: true
Read-only verification
Fetch the latest five message subjects from the main Exchange mailbox:
.venv/bin/python -m personal_mail_mcp.cli recent-messages exchange_primary --limit 5
The equivalent MCP tool exposed to Codex is:
microsoft_recent_messages(account_id, limit=5)
Gmail setup
Use a Google Cloud project for the Gmail API and OAuth desktop credentials.
-
Open Google Cloud Console:
https://console.cloud.google.com/ -
Create or select a project, then enable:
Gmail API -
Configure OAuth consent under:
Google Auth Platform > BrandingUse a simple app name such as
personal-mail-mcp. For personal Gmail accounts, useExternalaudience and add your Gmail address as a test user under:Google Auth Platform > Audience > Test users -
Create a desktop OAuth client under:
Google Auth Platform > Clients > Create clientUse:
Application type: Desktop app Name: personal-mail-mcp -
Download the OAuth client JSON and store it locally:
<repo-path>/.private/google-oauth-client.jsonTighten permissions:
chmod 600 .private/google-oauth-client.json -
Ensure
config/auth.tomlpoints to the file:[google] client_secrets_file = ".private/google-oauth-client.json" -
Connect the Gmail account:
cd <repo-path> PYTHONUNBUFFERED=1 .venv/bin/python -m personal_mail_mcp.cli connect google_primaryOpen the printed Google URL, sign in with the configured test user, and approve the Gmail read/modify scopes.
-
Verify:
.venv/bin/python -m personal_mail_mcp.cli statusThe Gmail account should show:
token_cached: true
Fetch the latest three Gmail inbox subjects:
.venv/bin/python -m personal_mail_mcp.cli recent-gmail google_primary --limit 3
The equivalent MCP tool exposed to Codex is:
gmail_recent_messages(account_id, limit=5)
Mail triage
The MCP server includes reusable inbox audit and archive helpers so repeated triage does not require ad hoc scripts.
Run a read-only audit across accounts:
cd <repo-path>
.venv/bin/python -m personal_mail_mcp.cli audit-mail exchange_primary exchange_secondary google_primary --limit-per-account 250
Create a dry-run archive plan. This returns only archive candidates grouped across all requested accounts by archive reason, sender, and normalized subject. Each message includes its account id, message id, subject, sender, and received date:
.venv/bin/python -m personal_mail_mcp.cli archive-plan exchange_primary exchange_secondary google_primary --limit-per-account 250
List a single inbox with pagination:
.venv/bin/python -m personal_mail_mcp.cli inbox exchange_primary --limit 100
Scan for unread mail outside the Inbox, such as archived or rule-moved messages. The command classifies those messages and returns attention candidates separately from obvious archive/noise:
.venv/bin/python -m personal_mail_mcp.cli missed-mail exchange_primary exchange_secondary google_primary --limit-per-account 100
Archive selected messages by id:
.venv/bin/python -m personal_mail_mcp.cli archive-mail exchange_primary <message-id> [<message-id> ...]
The equivalent MCP tools exposed to Codex are:
mail_inbox(account_id, limit=100)
mail_audit(account_ids, limit_per_account=250)
mail_archive_plan(account_ids, limit_per_account=250)
missed_mail(account_ids, limit_per_account=100)
archive_messages(account_id, message_ids)
The audit classifier is intentionally deterministic. It groups mail into
keep, flag, archive, and review. Use mail_archive_plan as the normal
review step before moving messages; it is read-only and contains the exact ids
needed for archive_messages. When the same archive pattern appears more than
once or across multiple accounts, the plan also returns filter recommendations
that can be used to create mailbox/provider rules for future messages.
Remote filter/rule creation is possible but requires additional OAuth scopes:
Microsoft Graph message rules require MailboxSettings.ReadWrite; Gmail filter
creation requires gmail.settings.basic. Until those are added and approved,
the server should only recommend filters rather than creating them.
Reusable defaults live in config/mail_rules.default.toml. User-specific
senders, retention counts, and archive patterns belong in
config/mail_rules.local.toml, which is ignored by git. Use
config/mail_rules.example.toml as a template for local overrides.
Optional Codex skill
This project includes a shareable Codex skill:
skills/email-appointment-harvest
skills/inbox-triage
skills/missed-mail-review
skills/review-all-mail
The skill documents the repeatable workflow for scanning recent email, proposing appointment/calendar candidates, waiting for approval, and then creating or updating only approved calendar entries.
Install it into a Codex environment:
mkdir -p "${CODEX_HOME:-$HOME/.codex}/skills"
cp -R skills/email-appointment-harvest "${CODEX_HOME:-$HOME/.codex}/skills/"
cp -R skills/inbox-triage "${CODEX_HOME:-$HOME/.codex}/skills/"
cp -R skills/missed-mail-review "${CODEX_HOME:-$HOME/.codex}/skills/"
cp -R skills/review-all-mail "${CODEX_HOME:-$HOME/.codex}/skills/"
Restart Codex after installing. Example request:
Use email appointment harvest to scan the last 7 days of email for appointments,
propose calendar entries, and add only the entries I approve.
The installed skill assumes this MCP server is configured in Codex and that the mail/calendar OAuth tokens have already been connected.
Security notes
config/auth.toml,config/accounts.toml, and.tokens/are local-only.- The Microsoft app is a public/native client; do not create or store a client secret for this CLI flow.
- Gmail client credentials under
.private/and token caches under.tokens/should be mode600for files and700for directories. - Microsoft
Mail.ReadWrite, Googlegmail.modify, and MicrosoftCalendars.ReadWriteare required for the current archive/calendar tools. Keep the app registration scoped to only the delegated permissions you use. - Treat
archive_messagesand calendar mutation tools as write actions. Prefermail_archive_planand calendar reads before applying changes. - Rotate any personal access tokens that were ever stored in plaintext config.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.