Files
skald-connectors/connectors/email/verify.py
T
dguiducci 1caba6946c Add Email (IMAP/SMTP) and SSH Remote Access connectors
- CLAUDE.md: developer guide for the marketplace repo
- email/: generic IMAP+SMTP connector (stdlib only, config via env)
  - env[] array, auth.type: password, verify.py (IMAP+SMTP probe)
- ssh/: SSH Remote Access connector ported from Skald, modified
  - ALIASES_FILE → ~/.ssh_aliases.json (was ./secrets/...)
  - optional env[] (TTL/timeout tunables)
  - auth.type: none (per-alias auth at runtime via elicitation)
- tavily/: restructured with env[] (tavilyApiKey), verify.py, auth block
- connectors.json: added email, ssh, and tavily verify.py entries
- SKALD.md: documented env[], verify, password auth, SSH connector
2026-07-16 22:29:33 +01:00

90 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""Verify the Email connector credentials by attempting IMAP and SMTP logins.
Reads the EMAIL_* environment variables (the same set the MCP server consumes),
opens one IMAP and one SMTP connection, attempts LOGIN on each, and prints a
single JSON object on stdout:
{"ok": true, "message": "...", "details": {"imap": "...", "smtp": "..."}}
{"ok": false, "message": "...", "details": {"imap": "...", "smtp": "..."}}
Exit code is 0 on success, 1 on any failure. No credentials are ever printed.
stdlib only (imaplib, smtplib, ssl) — mirrors the email_mcp_server.py constraint.
"""
import imaplib
import json
import os
import smtplib
import ssl
import sys
def _result(ok, message, **extra):
print(json.dumps({"ok": ok, "message": message, **extra}))
sys.exit(0 if ok else 1)
def _imap():
host = os.environ.get("EMAIL_IMAP_HOST", "").strip()
port = int(os.environ.get("EMAIL_IMAP_PORT", "993") or "993")
user = os.environ.get("EMAIL_USERNAME", "").strip()
pw = os.environ.get("EMAIL_PASSWORD", "")
if not host or not user or not pw:
return False, "EMAIL_IMAP_HOST, EMAIL_USERNAME and EMAIL_PASSWORD are required"
try:
ctx = ssl.create_default_context()
with imaplib.IMAP4_SSL(host, port, ssl_context=ctx) as imap:
imap.login(user, pw)
imap.select("INBOX", readonly=True)
return True, f"IMAP login to {host}:{port} successful"
except Exception as e:
return False, f"IMAP login to {host}:{port} failed: {e}"
def _smtp():
host = os.environ.get("EMAIL_SMTP_HOST", "").strip()
port = int(os.environ.get("EMAIL_SMTP_PORT", "465") or "465")
sec = os.environ.get("EMAIL_SMTP_SECURITY", "").strip().lower()
user = os.environ.get("EMAIL_USERNAME", "").strip()
pw = os.environ.get("EMAIL_PASSWORD", "")
if not host or not user or not pw:
return False, "EMAIL_SMTP_HOST, EMAIL_USERNAME and EMAIL_PASSWORD are required"
if not sec:
sec = "starttls" if port in (587, 25) else "ssl"
try:
ctx = ssl.create_default_context()
if sec == "ssl":
with smtplib.SMTP_SSL(host, port, context=ctx, timeout=15) as s:
s.login(user, pw)
elif sec == "starttls":
with smtplib.SMTP(host, port, timeout=15) as s:
s.starttls(context=ctx)
s.login(user, pw)
else:
with smtplib.SMTP(host, port, timeout=15) as s:
s.login(user, pw)
return True, f"SMTP login to {host}:{port} ({sec}) successful"
except Exception as e:
return False, f"SMTP login to {host}:{port} failed: {e}"
def main():
imap_ok, imap_msg = _imap()
smtp_ok, smtp_msg = _smtp()
if imap_ok and smtp_ok:
_result(True, "IMAP and SMTP authentication successful",
details={"imap": imap_msg, "smtp": smtp_msg})
if imap_ok and not smtp_ok:
_result(False, f"IMAP ok but SMTP failed: {smtp_msg}",
details={"imap": imap_msg, "smtp": smtp_msg})
if smtp_ok and not imap_ok:
_result(False, f"SMTP ok but IMAP failed: {imap_msg}",
details={"imap": imap_msg, "smtp": smtp_msg})
_result(False, f"Both IMAP and SMTP failed: {imap_msg} | {smtp_msg}",
details={"imap": imap_msg, "smtp": smtp_msg})
if __name__ == "__main__":
main()