Troubleshooting¶
Start with the privacy-safe diagnostic:
It checks the supported runtime and platform, configuration, state-directory permissions, credential lookup, and Bridge connectivity without printing email addresses, credentials, mailbox counts, or message content.
Quick symptom map¶
| Symptom | Likely layer | First check |
|---|---|---|
Plugin is installed but /mcp shows no Proton tools |
MCP server exited before tool discovery | Check the user-manager variables below |
PROTON_BRIDGE_USER is required |
Codex did not inherit the account variable | Reload environment.d and verify names |
list_sender_addresses returns only the primary address |
Alias forwarding is missing from the installed plugin or Codex started without the alias variable | Run the alias-specific checks below |
echo "$PROTON_BRIDGE_USER" is empty after a reload |
Current shell kept its old environment | Check systemctl --user show-environment; source the file only for a CLI test |
doctor passes through systemd-run but the plugin has no tools |
GNOME or ChatGPT kept an older environment | Fully quit, then launch ChatGPT once from the prepared terminal |
NoKeyringError appears only inside a sandbox |
That process cannot reach the desktop keyring | Run doctor from the normal user session; do not copy the password into plugin JSON |
| Bridge authentication fails everywhere | Bridge, port, or stored Bridge credential | Check Bridge and rerun setup if its credential changed |
Startup and configuration¶
Plugin is installed but /mcp shows no Proton tools¶
This means installation and MCP startup are different states. The bundled plugin can be present
while its local STDIO server exits before returning its tool list. OpenAI's Codex MCP configuration
distinguishes between env, which sets a value, and env_vars, which only allows and forwards a
value already present in Codex's local environment. Proton Safe deliberately uses env_vars so a
personal address is not committed to the plugin package.
Use this sequence on Ubuntu:
- Confirm the plugin is installed with the same verified Codex binary used during installation:
- Confirm the non-secret file exists and contains assignments in
KEY=VALUEform, withoutexport:
PROTON_BRIDGE_USER=your-address@proton.me
PROTON_BRIDGE_ALIASES=billing@example.com,legal@example.com
PROTON_IMAP_PORT=1143
- Reload the user environment generator:
- Verify only the variable names, without printing their values:
systemctl --user show-environment |
awk -F= '$1 == "PROTON_BRIDGE_USER" || $1 == "PROTON_BRIDGE_ALIASES" || $1 == "PROTON_IMAP_PORT" { print $1 "=<set>" }'
The two required names must appear, plus PROTON_BRIDGE_ALIASES when configured. Logging out
and back in is not a substitute for this check; the user manager may have retained its earlier
environment.
- Test the corrected user-manager environment with the plugin's pinned runtime:
This privacy-safe command separates environment, keyring, and Bridge failures before ChatGPT is involved. It does not prove that GNOME or an already-running ChatGPT process inherited the same variables.
-
Quit ChatGPT completely, including background processes, reopen it, start a new task, and type
/mcp. Theproton-safeserver should now list its tools. -
If the menu relaunch still has no tools, quit ChatGPT again. Confirm that no old application process remains:
Wait until the command prints nothing. Then launch ChatGPT from a terminal that explicitly loads the same non-secret file:
An Electron single-instance launch can hand control to an older process, so starting this
command before the previous instance exits will not repair its environment. After the app
opens, create a new task and check /mcp again.
Make the menu launch permanent¶
The terminal launch above is only a diagnostic. You do not need to type it every time. If it
restores the plugin, create a per-user launcher that keeps the normal ChatGPT desktop ID and
loads only the supported non-secret settings.
Create the private executable directory if needed:
Then create ~/.local/bin/chatgpt-proton-safe with this content:
#!/bin/sh
set -eu
settings_file="${XDG_CONFIG_HOME:-$HOME/.config}/environment.d/90-proton-safe.conf"
if [ ! -r "$settings_file" ]; then
printf 'ChatGPT launcher: cannot read %s\n' "$settings_file" >&2
exit 1
fi
proton_bridge_user=""
proton_bridge_aliases=""
proton_imap_port=""
# Parse only the non-secret settings; never evaluate the file as shell code.
while IFS='=' read -r setting_name setting_value; do
# environment.d accepts quoted values. Remove one matching outer quote pair
# without evaluating substitutions, commands, or backslash escapes.
case "$setting_value" in
\"*\") setting_value=${setting_value#\"}; setting_value=${setting_value%\"} ;;
\'*\') setting_value=${setting_value#\'}; setting_value=${setting_value%\'} ;;
esac
case "$setting_name" in
PROTON_BRIDGE_USER) proton_bridge_user=$setting_value ;;
PROTON_BRIDGE_ALIASES) proton_bridge_aliases=$setting_value ;;
PROTON_IMAP_PORT) proton_imap_port=$setting_value ;;
esac
done < "$settings_file"
if [ -z "$proton_bridge_user" ]; then
printf 'ChatGPT launcher: PROTON_BRIDGE_USER is missing from %s\n' "$settings_file" >&2
exit 1
fi
case "$proton_imap_port" in
''|*[!0-9]*)
printf 'ChatGPT launcher: PROTON_IMAP_PORT must be numeric in %s\n' "$settings_file" >&2
exit 1
;;
esac
export PROTON_BRIDGE_USER="$proton_bridge_user"
export PROTON_BRIDGE_ALIASES="$proton_bridge_aliases"
export PROTON_IMAP_PORT="$proton_imap_port"
exec /usr/bin/chatgpt "$@"
Make the script executable, copy the system desktop entry to the per-user application directory, and replace only its launch command:
chmod 0755 ~/.local/bin/chatgpt-proton-safe
mkdir -p ~/.local/share/applications
cp /usr/share/applications/chatgpt.desktop ~/.local/share/applications/chatgpt.desktop
sed -i "s|^Exec=.*|Exec=$HOME/.local/bin/chatgpt-proton-safe %U|" \
~/.local/share/applications/chatgpt.desktop
update-desktop-database ~/.local/share/applications
Verify that Ubuntu resolves the normal desktop ID to the per-user entry:
The default application should be chatgpt.desktop. Fully quit the stale ChatGPT process one last
time, then use the normal icon. Future menu and dock launches will load the Proton settings without
a terminal. The wrapper never reads or exports PROTON_BRIDGE_PASSWORD.
The per-user desktop entry shadows the packaged one. After a ChatGPT package upgrade, compare
/usr/share/applications/chatgpt.desktop with the per-user copy and refresh the copy if OpenAI
added desktop-entry fields; then reapply the Exec= replacement above.
Do not add PROTON_BRIDGE_PASSWORD to .mcp.json, environment.d, or a checked-in file. Do not
reinstall the Bridge or plugin unless the commands above identify an installation failure.
See the FAQ for why the current terminal may still show an empty variable and how to collect privacy-safe evidence.
list_sender_addresses returns only the primary address¶
This symptom is different from a missing Proton tool list. Sender aliases are optional, so the MCP
server can start normally and silently fall back to the primary address when
PROTON_BRIDGE_ALIASES does not reach it. Two independent conditions must both be true:
- the installed plugin's
.mcp.jsonmust includePROTON_BRIDGE_ALIASESinenv_vars; and - the Codex process must already contain
PROTON_BRIDGE_ALIASESwhen it starts the MCP server.
Codex's env_vars setting is an allowlist and pass-through, not a settings-file loader. Reinstalling
the plugin can repair the first condition, but it does not update the environment of an already
running ChatGPT process. Restarting ChatGPT can repair the second condition, but it cannot repair an
older installed plugin manifest that never forwarded the variable.
Use this privacy-safe sequence on Ubuntu:
- Confirm that the user manager knows the alias variable without printing its value:
- Check what the server receives outside ChatGPT:
doctor should report a primary address plus the configured alias count. If it reports the
primary address only, fix ~/.config/environment.d/90-proton-safe.conf, run
systemctl --user daemon-reload, and repeat the check.
- If
doctorsees the aliases butlist_sender_addressesstill returns only the primary address, reinstall the current plugin build so its cached.mcp.jsonincludes the alias pass-through:
- Fully quit ChatGPT, wait until
pgrep -a -x ChatGPTprints nothing, reopen it, and start a new task. If the normal menu launch still loses the aliases, use the terminal-launch diagnostic and the persistent menu launcher documented above.
This sequence distinguishes the v2.0.1 plugin-packaging defect from a stale desktop-process environment. It also explains why the failure displayed no startup error: the primary sender remained valid throughout.
ChatGPT desktop on Ubuntu cannot find codex¶
The ChatGPT desktop package can use Codex and share MCP configuration without installing a separate
system-wide codex command. On the Ubuntu package tested by this project, first verify the bundled
binary:
test -x /usr/lib/chatgpt/resources/codex
/usr/lib/chatgpt/resources/codex --version
/usr/lib/chatgpt/resources/codex plugin --help
/usr/lib/chatgpt/resources/codex plugin marketplace --help
/usr/lib/chatgpt/resources/codex plugin add --help
The /usr/lib/chatgpt/resources/codex path is package-specific and may change. Do not install a
same-named third-party package solely because the shell's command-not-found helper suggests it.
unexpected argument 'marketplace' or unrecognized subcommand 'add'¶
A different or older codex executable is probably first in PATH. Inspect every candidate and
require the plugin help to work:
type -a codex
codex --version
codex plugin --help
codex plugin marketplace --help
codex plugin add --help
If the Ubuntu ChatGPT bundled command passes the check, use its absolute path for installation:
/usr/lib/chatgpt/resources/codex plugin marketplace add fbossiere/proton-safe-mcp --ref main
/usr/lib/chatgpt/resources/codex plugin add proton-safe@personal
Bash still points to a removed /snap/bin/codex¶
Clear the command cache or start a fresh login shell:
If codex is still unavailable, use the verified app-owned absolute path instead of installing an
unrelated package.
The plugin is installed but ChatGPT cannot see its settings¶
An app launched from the Ubuntu desktop menu does not inherit variables exported later in an
unrelated terminal. Follow the complete procedure under
Plugin is installed but /mcp shows no Proton tools.
PROTON_BRIDGE_USER is required¶
Set the address in the environment inherited by the CLI or MCP client:
An integer setting is rejected¶
All numeric settings must be positive integers within the limits in Configuration. Remove the override to return to the safe default.
Bridge connection¶
No Proton Bridge password found¶
Ensure PROTON_BRIDGE_USER exactly matches the address used during setup, then run:
The credential is keyed by that address in the OS keyring.
Bridge login or IMAP connection fails¶
Check that:
- Proton Mail Bridge is installed, signed in, and running.
PROTON_IMAP_PORTmatches the IMAP port shown by Bridge.- The stored password is the Bridge-generated IMAP password, not the Proton account password.
- No sandbox prevents the local process from connecting to
127.0.0.1.
If Bridge regenerated its credential, run setup again.
Folders and messages¶
A folder cannot be opened¶
Call list_folders and pass the exact returned name. Folder input is validated and the mailbox is opened read-only.
A message is missing from a short list¶
Increase limit up to 100, use search_messages, or select a different folder. Results are newest first.
A body is truncated¶
Increase max_chars on read_message, up to 100000. Large output remains bounded by design.
Attachments¶
Why does received attachment extraction fail?¶
Call read_message first and copy the exact zero-based attachment_index. The extraction tool
supports only PDF, TXT, and CSV files. It rejects encrypted or malformed PDFs, unsupported MIME
types, attachments above PROTON_MCP_MAX_RECEIVED_ATTACHMENT_BYTES, and indexes that do not exist.
A scanned PDF can succeed with empty text because Proton Safe does not perform OCR or render pages. Ask the user to provide the file explicitly to a separate visual or OCR workflow when that is required; do not use shell, browser automation, or filesystem tools to export it from Proton Mail.
See Common attachment failures for filename, MIME, ordering, size, hash, and expiry errors.
Do not retry one chunk with the same index after the server has accepted it. Resume with the returned next_chunk, or discard and restart the upload if client state is uncertain.
Drafts¶
Explicit user confirmation of the exact draft is required¶
create_confirmed_draft requires user_confirmed: true. Present the exact recipients, subject,
complete body, and attachment list, obtain confirmation in the conversation, then call the tool
again with the unchanged values.
Use a bare email address, without display name¶
Recipients must be bare addresses such as person@example.com. Strip any Display Name <…>
wrapper before calling the tool.
A draft was created but is not in Drafts¶
Check the folder name Bridge exposes with list_folders. Some Proton locales expose a translated
Drafts folder.
Documentation build¶
Install the documentation extra and build with warnings treated as errors:
Preview locally with:
A reply draft appears outside the original conversation¶
Proton Bridge discards the parent link when it recreates an IMAP draft. Adding Re: to the
subject or receiving created: true does not establish that the draft belongs to the thread.
Starting with 2.1.1, Proton Safe refuses reply targets by default before creating a draft.
Prepare the answer, open the original message in Proton Mail, choose Reply or Reply all,
and paste it there. If a possibly separate draft is useful, explicitly accept that fallback;
the client can then set allow_unthreaded_reply: true along with the confirmed content.
Never retry by silently dropping the reply target. No send or mailbox-mutation capability is
needed for either workflow. See reply details.