Troubleshooting¶
Version scope: Managed mode and the embedded React UI on this page are Local Email App V2 behavior. See Version availability before using these commands with a PyPI installation.
Start by running the relevant command with a visible terminal so server logs and keyring prompts are not hidden by the MCP client.
Set a more detailed log level when needed:
MCP_EMAIL_SERVER_LOG_LEVEL=DEBUG mcp-email-server stdio
Restart the server after changing configuration paths or environment variables.
The server reports Missing command¶
The CLI requires a subcommand. Use one of:
mcp-email-server stdio
mcp-email-server sse
mcp-email-server streamable-http
mcp-email-server ui
For local development, use uv run mcp-email-server stdio rather than
uv run mcp-email-server.
An environment account does not appear¶
An environment-provided account requires all three variables:
MCP_EMAIL_SERVER_EMAIL_ADDRESS
MCP_EMAIL_SERVER_PASSWORD
MCP_EMAIL_SERVER_IMAP_HOST
The generic password must be non-empty and remains required even when
MCP_EMAIL_SERVER_IMAP_PASSWORD is set. An absent or empty IMAP/SMTP-specific
password falls back to the generic password. Invalid integer ports or invalid
account fields cause the environment account to be skipped and an error to be
logged.
If the environment account has the same MCP_EMAIL_SERVER_ACCOUNT_NAME as a
TOML account, it replaces that entire account for the current process rather
than merging individual fields.
A different configuration file is loaded¶
The default legacy source and derived bootstrap authority are:
~/.config/mcp-email-server/config.toml
~/.config/mcp-email-server/config.bootstrap.toml
MCP_EMAIL_SERVER_CONFIG_PATH selects another legacy source path; the bootstrap
sidecar is its sibling with .bootstrap inserted before the TOML suffix. The
path is resolved when the configuration module is imported, so restart the server
after changing it. Selection and reviewed import do not rewrite the source file.
On first use, the server can copy a legacy file from:
~/.config/zerolib/mcp_email_server/config.toml
Check server logs for the resolved path.
Managed mode does not start¶
Run bounded diagnostics from a terminal:
mcp-email-server config status
mcp-email-server config doctor
# For agent/user automation:
mcp-email-server config status --json
mcp-email-server config doctor --json
The low-level agent management API uses schema_version: 1. JSON callers should
branch on schema_version, ok, command, typed error.code, and stable data
rather than matching fixed safe message prose. JSON output grants no authority to
run another command. Managed startup requires all of the following:
- a parseable private bootstrap sidecar with
bootstrap_version = 1,managed_selection = true,mode = "managed", andmanaged_db_location; - a present, regular, non-link SQLite file in a private immediate parent under the active POSIX or Windows profile;
- the exact supported managed schema.
The server deliberately does not fall back to TOML accounts when a bootstrap or
catalog check fails. An incomplete enabled account is omitted individually and
reported by config doctor; it does not block other complete accounts. Active
credentials are resolved only immediately before constructing that account's
provider, from the private managed SQLite secret store on Linux and Windows or
the same macOS system-keyring session. An unreadable secret
therefore fails that account operation and is reported by config doctor, rather
than blocking unrelated complete accounts at startup. config status still
returns bounded bootstrap state and
catalog_status=unavailable when the selected database is missing, corrupt,
incompatible, or insecure. A fresh installation reports
catalog_status=not_configured; an agent must hand setup back to the user rather
than collecting credentials. In an unavailable state, deliberately run mcp-email-server
config select legacy and restart; this recovery transition uses a revisioned
bootstrap compare-and-swap and does not open the failed catalog. If the bootstrap
sidecar itself is unparseable, repair or restore that sidecar manually; reset
cannot safely infer its mode and therefore does not unlink the independent legacy
source.
There are no released managed-catalog users for this pre-release redesign, so
older development catalog schemas are rejected rather than migrated. While
legacy mode is selected, preserve the old file for rollback and initialize a
fresh owner-only path with mcp-email-server config init --database NEW_PATH.
Then re-enter accounts or use the reviewed legacy import flow. Fresh setup
selects managed immediately; an existing v1 source remains selected until a
complete import succeeds. Remove the obsolete development catalog only after verifying the
replacement; on Linux and Windows, treat every old catalog copy as
secret-bearing.
If a managed password save fails, correct the reported storage or revision problem and submit a new value with:
mcp-email-server account set-secret ACCOUNT incoming
Use outgoing for an SMTP credential. A failed save leaves no intermediate
binding and does not change the current binding authority. On Linux and Windows,
secret insertion and active binding/revision commit in one managed SQLite
transaction. Check private catalog access and, on Windows, verify local fixed
NTFS and DACL security; on macOS, restore system-keyring access before retrying.
Provider connectivity is diagnosed with mcp-email-server account test ACCOUNT
incoming|outgoing [--json]; this agent-facing low-level CLI diagnostic has no
Web UI route or Test connection action. Connectivity checks report only bounded
categories: timeout, endpoint_unavailable, credential_unavailable,
authentication_or_provider_rejected, or tls_or_connection_failed. Follow the
safe remediation message; raw provider exceptions are intentionally hidden.
CLEANUP_REQUIRED means a replacement or detachment committed but an old value
could not be deleted. Restore access to the selected managed secret store and
run:
mcp-email-server config cleanup-credentials --limit 100
Cleanup handles only superseded cleanup-required rows and never removes an active credential. The account remains usable after a rotation cleanup failure; a credential detachment instead leaves the disabled account incomplete until a new secret is installed.
A managed write reports a revision conflict¶
Run mcp-email-server account show ACCOUNT and use its current revision with
--expected-revision. Update, disable, enable, credential removal, and soft
removal use optimistic revisions so a stale operator command cannot overwrite a
concurrent lifecycle or endpoint change. Do not blindly retry: inspect the new
state first, then issue the intended command against that revision.
To remove an account, the confirmation must also exactly match the current name:
mcp-email-server account remove work \
--expected-revision 7 \
--confirm work
The operation is a soft removal. Its normalized-name tombstone permanently reserves that name in this delivery; it cannot be reused.
Metadata index warnings or query_too_broad¶
In legacy mode, an owner, permission, symlink, busy, corrupt, or unsupported
schema problem at db_location disables only the rebuildable metadata index.
The application logs a bounded warning and runs the same request through IMAP;
the MCP handler does not bypass the application query service. Correct the
parent directory and database to owner-only access, or remove a disposable
operational database while the server is stopped so it can be rebuilt.
Managed mode is different because the selected database also owns account
authority and, on Linux and Windows, contains plaintext managed values in
managed_secret. A copied or backed-up catalog on either platform must retain
private protection equivalent to the original and must not be shared as a
non-secret diagnostic artifact. An
open, security, corruption, schema, or projection-write failure
therefore fails closed rather than returning a result or falling back to TOML.
In legacy mode, a projection write failure after a validated bounded provider
read may return that provider result with a warning; the next request refreshes
again.
query_too_broad means an IMAP search returned more than 10,000 candidate UIDs,
so the application could not prove the requested page and exact filtered total
within its work budget. Narrow the mailbox or add a date, sender, recipient,
subject, body, text, flag, or attachment filter. Increasing page_size cannot
bypass the limit; page_size is restricted to 1 through 100. Some providers,
including iCloud, omit the untagged empty SEARCH response and return only a
successful tagged completion line; this known response shape is treated as zero
matches. An invalid UID search results or incomplete provider-metadata error
means the server returned another malformed UID set or did not return exact
sender/INTERNALDATE evidence for every requested UID. The request is rejected
rather than expanding a UID range or returning an incorrect page; retry after
the mailbox is stable or report the provider issue.
Non-ASCII subject, body, text, sender, or recipient filters are sent as
synchronizing UTF-8 IMAP literals with CHARSET UTF-8. If a provider rejects the
charset, the search fails without rewriting or dropping the filter. Use the
provider's supported search syntax or an ASCII/narrower criterion; do not assume
that changing the process locale will help, because IMAP date months are always
protocol-defined English tokens.
The UI cannot load or authenticate¶
Run mcp-email-server ui in a visible terminal and keep that foreground process
running. Open only the fresh browser link launched by that process. If browser
launch fails, the command prints the one-time URL to that attached terminal. To
suppress browser launch deliberately, use --no-open in a real TTY; redirected
or noninteractive stdout/stderr is rejected and never receives the token. A bootstrap
link is single-use and expires after five minutes; replay, a stale tab after
restart, localhost substitution, a foreign Origin, or a copied URL whose
fragment was stripped produces the same bounded recovery message. Close the tab
and launch the command again rather than editing the process route or cookie.
The server accepts only exact 127.0.0.1:<actual-port> requests. A proxy,
browser extension, security product, or custom hosts rewrite that changes Host,
Origin, Fetch Metadata, JSON content type, cookie, or CSRF headers is rejected.
After authentication, ordinary account work is under Email accounts;
importing earlier settings, sending/attachment safety, and troubleshooting are
folded under Settings & help. Account creation starts with an email address
and password. The suggested server settings remain editable under the account
form's connection disclosures, together with login name, port, security,
certificate, sending, and Sent-folder details. There is no redundant connection
preview; advanced settings and optional outgoing mail stay folded until needed.
Provider connectivity testing is CLI-only. Empty-workspace and settled-ready
banners with no next action are hidden; actionable import, selection, restart,
or conflict states remain visible. There is no catalog activation step, and a
saved account is not a provider-connectivity certification.
There is no supported remote, wildcard, CORS, or shared-link mode. Managed catalog/bootstrap operations, attachment writes, and oversized spill
require the documented POSIX profile or a local fixed NTFS Windows path. An
unsupported-platform/filesystem error is a fail-closed boundary, not a
permissions setting that can be bypassed.
If Import existing settings reports that the managed catalog parent must be owner-only, an existing legacy configuration directory grants group or world access. Stop every server process, verify that the directory is owned by the current user and is dedicated to this application, then restrict that directory before retrying. For the default location on POSIX systems:
chmod 700 ~/.config/mcp-email-server
Do not apply this command to a shared directory or change ownership/permissions without first inspecting the path. The application deliberately does not chmod an existing legacy directory on the user's behalf.
If status loads but managed operations fail, run the equivalent bounded CLI checks in the same operating-system login session:
mcp-email-server config status
mcp-email-server config doctor
A storage failure prevents credential installation but leaves the current
binding authority unchanged. On Linux and Windows, check managed database
access; on Windows also verify that the catalog is on local fixed NTFS with its
private DACL intact. On macOS, restore system-keyring access. Return to Email
accounts, open Password for the affected account,
and submit a new value.
CLEANUP_REQUIRED means the active result is known but an old superseded value
remains. Email accounts shows a bounded password-data cleanup action whenever
doctor reports such leftovers, including after the last active account was
removed; you can also use the CLI. Revision conflicts are not
retried automatically: inspect
the displayed current summary before resubmitting. If a write succeeds but the
following account-list refresh fails, the UI hides the older account actions
instead of reusing stale revisions. Choose Refresh accounts before making
another change.
Keychain repeatedly asks for permission¶
On macOS, Keychain access can be associated with the application path. uvx
may resolve a new executable path after an update, causing another prompt.
Grant the appropriate persistent permission when prompted or install the
package at a stable path and point the MCP client to that executable.
A keyring-stored secret cannot be resolved¶
The error identifies the service and entry, for example:
service: mcp-email-server
entry: work:incoming
Check that:
- The keyring is unlocked and available in the server's session.
- The entry was not removed by another application or cleanup operation.
- The server process has access to the same keyring as the configuration UI.
- A macOS Keychain access prompt is not waiting behind another window.
Re-add the account if the referenced secret no longer exists.
credential_storage is plaintext but the file contains __KEYRING__¶
The file references keyring entries while the active mode refuses to resolve them. Use one of these approaches:
- Remove the
MCP_EMAIL_SERVER_CREDENTIAL_STORAGE=plaintextoverride. - Change the stored mode back to
autoorkeyringlong enough to load it. - Run
mcp-email-server migrate-credentials --to plaintextwhile the keyring is accessible.
Do not replace __KEYRING__ with an unknown value; it is only a marker.
Credential migration appears to have no effect¶
Check MCP_EMAIL_SERVER_CREDENTIAL_STORAGE. If it remains set, every later run
uses that value even when a migration wrote a different mode to the TOML file.
The migration command prints a warning when the values conflict.
Migration changes only persistent TOML accounts. It does not migrate an account supplied solely through environment variables.
send_email or forward_email reports that SMTP is unavailable¶
send_email and forward_email are always advertised in the static MCP
catalog, and both fail their SMTP capability check for an account without an
outgoing endpoint — a forward is refused before its source message is even
read. If sending fails for one account, confirm that the selected account is enabled and has a complete
SMTP endpoint and active outgoing credential. In managed mode, inspect it with
account show; disable it before changing or removing credentials, then
re-enable it with the latest revision. Run account test ACCOUNT outgoing to
authenticate and verify that the provider accepts the configured account email
address in MAIL FROM; the command then issues RSET without a recipient or
message body. A successful check does not prove later RCPT TO or DATA
acceptance.
The configured full name is a display name, not an envelope address. Values that
contain @, commas, quotes, or non-ASCII text are quoted or encoded in the
message From header, while SMTP uses only the separate account email address.
A partial or failed send_email result includes reviewed fixed tags such as
smtp-mail-rejected, smtp-recipient-rejected, or smtp-data-rejected when
available. smtp-utf8-unsupported means an envelope addr-spec or an address or
thread header requires internationalized syntax but the SMTP server did not
advertise SMTPUTF8; the server rejects before issuing MAIL FROM, RCPT TO, or
DATA. Use an ASCII addr-spec/header value or a provider with SMTPUTF8 support.
A non-ASCII display name attached to an ASCII address does not trigger this
requirement.
smtp-8bitmime-required means either a correctly labeled 8bit MIME body needs
raw high-bit transport or an SMTPUTF8 message is subject to RFC 6531's mandatory
8BITMIME pairing, but the server did not advertise 8BITMIME. Use a provider
with that extension or, when SMTPUTF8 is not otherwise required, compose a
7-bit-safe message whose parts use base64 or quoted-printable.
smtp-mime-transport-invalid means raw high-bit payload bytes
do not match their declared transfer encoding, so enabling 8BITMIME would not
make the MIME entity valid; correct or re-encode that source part.
smtp-binarymime-unsupported means the message requires a binary transport path,
for example because it declares a binary transfer encoding or contains NUL or
DATA framing that ordinary line-oriented SMTP cannot carry. This client does not
implement BINARYMIME with CHUNKING/BDAT; re-encode the affected leaf part as
base64 before sending. Both failures happen before MAIL FROM, so they are known
failures and must not be treated as ambiguous delivery. Results never include the
provider's free-form response text.
For delivery diagnostics, enable DEBUG and inspect the bounded SMTP records.
phase=connect and phase=authenticate cover session setup; phase=mail,
phase=rcpt, and phase=data identify explicit SMTP commands, while
phase=transaction covers other transaction preparation; phase=send is the
legacy aggregate path; and phase=cleanup occurs after a known delivery outcome. A numeric code is the SMTP response status. A category is a fixed
transport class rather than raw exception or provider text. Logs intentionally
omit usernames, addresses, subjects, bodies, raw MIME, attachments, and provider
response strings at every level. Use the provider's own delivery logs when its
free-form rejection explanation is required.
SMTP delivery succeeds but saving to Sent fails¶
SMTP delivery and the IMAP append are separate operations. A tagged result can
therefore show accepted recipients together with sent-copy: failed or
sent-copy: unknown. Do not resend the message to repair the copy. List the
provider's folders with list_mailboxes, then configure the exact folder:
[[emails]]
account_name = "work"
save_to_sent = true
sent_folder_name = "INBOX.Sent"
Set save_to_sent = false if the provider already stores sent messages and an
additional append is unnecessary.
For a message with an internationalized addr-spec or thread-header identifier,
utf8-append-unsupported means the IMAP server did not advertise and positively
enable the RFC 6855 UTF8 mode before mailbox selection. SMTP delivery may still
have succeeded. Do not resend; inspect the provider-managed Sent folder, disable
the extra copy when the provider already saves one, or use an IMAP endpoint that
supports ENABLE with UTF8=ACCEPT/UTF8=ONLY.
IMAP reports a malformed ID command¶
mcp-email-server sends at most one compact RFC 2971 ID command after login,
and only when the IMAP server advertises the ID capability. This form supports
strict parsers such as NetEase while avoiding an optional command on providers
that do not implement the extension.
If logs still show BAD malformed command, IMAP ID command failed, or a
subsequent provider_failure, first upgrade to a release containing the latest
IMAP compatibility fixes. Then run mcp-email-server account test ACCOUNT
incoming from a visible terminal. If it still fails, report the provider, server
hostname, application version, and sanitized IMAP command/response sequence.
Never include the username, password, message data, or authentication payload.
IMAP or SMTP TLS fails¶
Verify that the port and TLS mode match the provider:
| Connection | Common settings |
|---|---|
| IMAP implicit TLS | Port 993, use_ssl = true, start_ssl = false |
| IMAP STARTTLS | Port 143, use_ssl = false, start_ssl = true |
| SMTP implicit TLS | Port 465, use_ssl = true, start_ssl = false |
| SMTP STARTTLS | Port 587, use_ssl = false, start_ssl = true |
Do not enable both implicit TLS and STARTTLS. Disable certificate verification only for a trusted local endpoint with a known self-signed certificate.
For ProtonMail Bridge, copy the host, ports, username, and password shown by the bridge rather than using the normal account password.
Attachment download is denied¶
The tool is visible even when permission is disabled. Enable it explicitly:
enable_attachment_download = true
Or:
MCP_EMAIL_SERVER_ENABLE_ATTACHMENT_DOWNLOAD=true
Normally omit save_path. The server then creates a safe randomized file under the
current user's Downloads/mcp-email-server directory and returns its absolute
path. It securely creates missing components and gives the application child
private permissions; it does not change permissions on the general Downloads
directory. Windows accepts a redirected Downloads Known Folder when it remains
on safe local fixed NTFS storage and falls back to the profile's ~/Downloads
when the registry value is unavailable, malformed, or non-absolute. If the resolved location
fails the platform security profile, use an explicit safe local destination
instead.
For an explicit destination, use an absolute save_path when possible and
ensure the server process can write to its parent directory. A relative path is
resolved against the server process's working directory. Filesystem support is
checked before provider fetch. The destination fails closed if any parent is a
symlink/reparse point or not a directory, or if the target is linked,
permissive, a FIFO/device, or another non-regular object.
On Windows, use an ordinary local fixed NTFS drive-letter path with a parent
directory below the volume root; direct C:\\file-style storage is unsupported.
For an explicit path, the immediate parent must not grant another SID permission
to create, modify, delete, or change security on entries. Read-only ACEs do not
need to be removed. The default application child avoids requiring the Downloads
folder itself to satisfy this sensitive-parent rule. Junctions and all reparse
tags are rejected along with UNC/mapped network paths, \\?\\/\\.\\ device
paths, alternate streams such as file:stream, and FAT/exFAT. Move the
destination to local NTFS rather than bypassing the check. Old application temp
files are deleted only after bounded owner/DACL/type/link/identity validation;
a substituted or unverified entry is deliberately left untouched.
A message mutation reports success but nothing changed¶
With allowed_senders configured, blocked message IDs are reported as
successful no-ops by default. This prevents callers from using mutation results
to discover hidden messages.
To report blocked IDs as failures instead:
report_blocked_mutations = true
Also confirm that the email_id belongs to the mailbox supplied to the
mutation tool.
Archive folder cannot be found¶
archive_emails first looks for an RFC 6154 \Archive flag and then checks
Archive, Archives, and [Gmail]/All Mail.
Call list_mailboxes to discover the actual folder and use move_emails with
an explicit destination when the provider uses another name.
Delete or move reports failures on an older IMAP server¶
Message-scoped delete requires IMAP UIDPLUS and uses target-scoped
UID EXPUNGE. It deliberately never falls back to mailbox-wide EXPUNGE,
because that could remove unrelated messages already marked \Deleted by
another client.
When a server lacks both native MOVE and UIDPLUS, move_emails also rejects
the COPY-and-delete fallback before copying. Use the provider's native client or
upgrade/configure the server to support MOVE or UIDPLUS.
A mutation result contains unknown or reconciliation needed¶
unknown means the remote effect may have started but the connection did not
return authoritative completion evidence. The server deliberately does not
retry. Inspect the target mailbox, flags, Message-ID, or provider delivery
records before deciding whether a narrow manual retry is safe.
reconciliation needed means the remote outcome is known, but invalidating the
local metadata projection failed. The projection is disposable; correct the
operational database problem and refresh metadata. Do not undo or repeat the
provider effect merely to repair local index state.
HTTP requests are rejected by Host or Origin validation¶
For a container, proxy, or non-loopback hostname, configure the names seen by the server:
MCP_ALLOWED_HOSTS='mail-mcp.example.com,mcp-email-server'
MCP_ALLOWED_ORIGINS='https://mail-mcp.example.com'
A wildcard bind such as 0.0.0.0 does not tell the server which public
hostname a request will use. Do not disable DNS rebinding protection merely to
avoid configuring an explicit allowlist.
Legacy import reports a conflict or missing credential¶
Run mcp-email-server config import-legacy without --apply to preview again.
A conflict means the managed destination differs from the effective legacy
account, collides after managed name normalization, or retains that name from a
soft removal. Planning also rejects normalized collisions within the source and
account-limit overflow. Import checks all such conditions before resolving
secrets or writing, and it will not overwrite the destination. Use a fresh
database or reconcile the destination manually.
Preview includes complete environment-only accounts and environment policy
overrides with legacy runtime precedence, but never reads their secret values or
the keyring. Run config import-legacy --apply, review the full non-secret plan,
and type IMPORT only when a changed plan prompts. Apply reads required current
TOML, environment, or keyring credentials. If it reports a missing credential,
unlock or repair the legacy keyring entry or restore the environment value and
repeat the reviewed apply. A stale-preview error means the effective source,
selected catalog path/bootstrap revision, or an exact catalog, policy, or
account target revision changed; create and review a new preview rather than
retrying an old confirmation. Matching account rows are reused and only missing
bindings are filled. Once every source account type is supported, even when every
row is already present, run --apply without a confirmation prompt or choose
Finish setup: finalization privately verifies that each active managed
password equals the current legacy password before cutover.
import_credential_conflict means they differ; update or remove the
managed credential and preview again. Import does not guess which password should
win and overwrites neither side. A failed credential save leaves destination
authority unchanged and legacy runtime selected; cleanup-required results need
the reported cleanup. A fully successful
import selects managed automatically only when all source account types are
supported. Unsupported providers keep legacy selected until the user explicitly
chooses otherwise. TOML, environment, and legacy keyring entries are never
deleted.
Duplicate account name¶
Account names must be unique across all stored account types. Choose a new
account_name. Soft removal does not release a name: its normalized tombstone
remains reserved permanently in this delivery.
An environment account with the same name as a TOML email account is the one exception: it intentionally replaces that account in the runtime view.
Collect information for a bug report¶
Include:
- Operating system and version.
- Python and
mcp-email-serverversions. - Installation method, such as
uvxorpip. - Transport and MCP client.
- IMAP/SMTP provider and TLS mode, without credentials.
- Relevant logs with email addresses, message contents, tokens, and passwords removed.
- Minimal steps to reproduce the problem.
Report issues at https://github.com/Wh1isper/mcp-email-server/issues.