
Send Email
- 646 installs
- 8 repo stars
- Updated August 4, 2026
- wangyendt/wayne-skills
send-email is an agent skill that sends SMTP email from development workflows using a Python scripts/send_email.py helper with plain text, HTML, Markdown conversion, attachments, and template variable substitution.
About
send-email is an agent skill in wangyendt/wayne-skills that automates SMTP delivery through scripts/send_email.py. Agents walk an interactive provider setup for Gmail, Outlook, QQ Mail, 163/126 Mail, or custom SMTP servers, auto-convert Markdown bodies to styled HTML, attach files, and render template files with JSON variable substitution. The skill defaults to agent_skill_test@126.com when no sender is specified and confirms credentials and recipients before sending. Developers reach for send-email when they need agent-driven notification emails, HTML reports, or attachment delivery during builds without wiring a full transactional email SDK first.
- send-email
Send Email by the numbers
- 646 all-time installs (skills.sh)
- +1 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #582 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/wangyendt/wayne-skills --skill send-emailAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 646 |
|---|---|
| repo stars | ★ 8 |
| Last updated | August 4, 2026 |
| Repository | wangyendt/wayne-skills ↗ |
How do you send SMTP email from an agent script?
Use send-email for development tasks
Who is it for?
Developers who need agent-assisted SMTP sends with Markdown-to-HTML conversion, attachments, and multi-provider setup during integration work.
Skip if: Production bulk marketing email, Resend or SendGrid API-first workflows, or teams that cannot provide SMTP credentials to an agent.
When should I use this skill?
A user asks to send an email, email a report, attach files to a message, or configure Gmail, Outlook, or QQ SMTP from a project.
What you get
Delivered SMTP messages, rendered HTML from Markdown or templates, attached files, and logged send confirmations with provider-specific settings.
- Sent SMTP email messages
- Rendered HTML from Markdown or templates
- Configured provider SMTP settings for repeat sends
By the numbers
- Uses scripts/send_email.py as the primary delivery entrypoint
- Documents 5 SMTP provider presets: Gmail, Outlook, QQ, 163, and 126 Mail
- Supports multiple --attach file paths on a single send command
Files
Send Email
Send emails via SMTP using Python with support for plain text, HTML formatting, file attachments, and template-based emails.
Key Features
- Default Sender Email: Automatically uses
agent_skill_test@126.comwhen no sender is specified - Automatic Markdown to HTML: Detects Markdown content and converts to beautifully styled HTML
- Multiple Email Providers: Supports Gmail, Outlook, QQ Mail, 163/126 Mail, and custom SMTP
- File Attachments: Attach multiple files to emails
- Email Templates: Use template files with variable substitution
Interactive Email Sending Flow
This skill guides you through the email sending process step by step.
Step 1: Collect Basic Email Information
Ask the user for the following required information. If any is missing, prompt the user to provide it:
| Information | Description | Example |
|---|---|---|
Recipient (--to) | Email address to send to | user@example.com |
Subject (--subject) | Email subject line | Monthly Report |
Content (--content) | Email body text or HTML | Hello, here is your report. |
IMPORTANT - Default Sender Email Behavior:
If the user does NOT specify a sender email in their initial request, AUTOMATICALLY use `agent_skill_test@126.com` as the default sender. DO NOT ask the user which email to send from.
Only ask the user for a different sender email if they explicitly mention wanting to use their own email address.
When using the default agent_skill_test@126.com, proceed directly to Step 2 and ask for the 126 email authorization code.
Step 2: Identify Email Provider and Collect Credentials
Based on the sender's email address domain, identify the email provider and request the appropriate credentials.
Email Provider Detection
@gmail.com, @googlemail.com → Gmail
@outlook.com, @hotmail.com, @live.com, @office365.com → Outlook/Office 365
@qq.com, @vip.qq.com → QQ Mail
@163.com, @126.com → NetEase Mail
@aliyun.com, @aliyun-inc.com → Aliyun Mail
@sendgrid.net, @*.sendgrid.net → SendGrid
@*.mailgun.org → Mailgun
Other domains → Custom SMTP (ask for server, port, username, password)Provider-Specific Credential Requirements
Gmail (@gmail.com, @googlemail.com)
- Password Type: App Password (NOT account password)
- Prerequisite: Two-factor authentication (2FA) must be enabled
- How to get App Password:
1. Go to https://myaccount.google.com/apppasswords 2. Sign in to your Google Account 3. Select "Mail" and "Other (Custom name)" → Enter a name like "SMTP Script" 4. Click "Generate" → Copy the 16-character password
- SMTP Server:
smtp.gmail.com - Port:
587(TLS) or465(SSL) - Username: Your full Gmail address
Outlook / Office 365 (@outlook.com, @hotmail.com, @live.com, @office365.com)
- Password Type: Account password (same as web login)
- SMTP Server:
smtp.office365.com - Port:
587(TLS) - Username: Your full Outlook/Office 365 email address
QQ Mail (@qq.com, @vip.qq.com)
- Password Type: SMTP Authorization Code (NOT account password)
- How to get Authorization Code:
1. Login to QQ Mail at https://mail.qq.com 2. Click "Settings" (设置) → "Account" (账户) 3. Find "POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV Service" 4. Open "POP3/SMTP Service" or "IMAP/SMTP Service" 5. Click "Generate Authorization Code" (生成授权码) 6. Verify identity via SMS → Copy the generated code
- SMTP Server:
smtp.qq.com - Port:
587(TLS) or465(SSL) - Username: Your full QQ email address
NetEase 163 Mail (@163.com)
- Password Type: SMTP Authorization Code (NOT account password)
- How to get Authorization Code:
1. Login to 163 Mail at https://mail.163.com 2. Click "Settings" (设置) → "POP3/SMTP/IMAP" 3. Click "Open" next to "IMAP/SMTP Service" 4. Verify identity → Click "Get Authorization Code" (获取授权码) 5. Copy the 16-character authorization code
- SMTP Server:
smtp.163.com - Port:
465(SSL) or994(IMAP) - Username: Your full 163 email address
NetEase 126 Mail (@126.com)
- Password Type: SMTP Authorization Code (NOT account password)
- How to get Authorization Code:
1. Login to 126 Mail at https://mail.126.com 2. Click "Settings" (设置) → "POP3/SMTP/IMAP" 3. Enable "IMAP/SMTP Service" 4. Copy the authorization password displayed
- SMTP Server:
smtp.126.com - Port:
465(SSL) or25(SSL) - Username: Your full 126 email address
SendGrid
- Password Type: API Key (as password)
- How to get API Key:
1. Login to SendGrid at https://app.sendgrid.com 2. Go to Settings → API Keys 3. Click "Create API Key" 4. Set permissions → Click "Create & View" 5. Copy the API key (only shown once)
- SMTP Server:
smtp.sendgrid.net - Port:
587(TLS) or465(SSL) - Username:
apikey(literally this string)
Mailgun
- Password Type: SMTP credentials from dashboard
- How to get Credentials:
1. Login to Mailgun at https://app.mailgun.com 2. Go to Sending → Domains 3. Select your domain 4. Copy SMTP username and password from the page
- SMTP Server:
smtp.mailgun.org - Port:
587(TLS) - Username: From Mailgun dashboard
Custom SMTP (Other domains) Ask the user to provide:
- SMTP Server Address
- SMTP Port
- Username
- Password
Step 3: Auto-detect and Handle Markdown Content
The script automatically detects if the email content is in Markdown format by looking for common patterns:
- Headers (
#,##,###) - Bold/italic text (
**bold**,*italic*) - Lists (
- itemor1. item) - Code blocks (```)
- Links (
[text](url))
If Markdown is detected, the content is automatically converted to beautifully styled HTML with:
- Professional typography and spacing
- Color-coded headers
- Styled tables and code blocks
- Responsive design for email clients
You don't need to manually specify `--content-type html` for Markdown content - it's handled automatically!
Step 4: Collect Optional Information (if needed)
--from-name: Display name for sender (e.g., "John Doe")--content-type html: For HTML formatted emails (defaults to plain text, but auto-converts Markdown)--attach: File path(s) to attach (can specify multiple)--template: Use template file instead of content--template-vars: JSON string of template variables
Step 5: Confirm and Send
Before sending, confirm with the user:
Ready to send:
From: [Sender Email]
To: [Recipient Email]
Subject: [Subject]
Content Type: [Plain/HTML]
Attachments: [List of attachments or None]
Proceed to send? (y/n)If confirmed, execute the send_email.py script with all collected parameters.
Example Conversations
Example 1: Basic Gmail Email
User: Send an email to john@example.com with subject "Hello"
Skill: I can help you send that email. I need a few more details:
1. Content: What should the email body contain? 2. Sender Email: What email address should I send from?
User: The content is "Hi John, just saying hello!" and send from myemail@gmail.com
Skill: I detected you're using Gmail. For Gmail, you need an App Password (not your regular password).
To get your App Password: 1. Go to https://myaccount.google.com/apppasswords 2. Sign in and create an App Password for "Mail" 3. Copy the 16-character password
Please provide your Gmail App Password:
User: abcd efgh ijkl mnop
Skill: Ready to send:
- From: myemail@gmail.com
- To: john@example.com
- Subject: Hello
- Content: Hi John, just saying hello!
Proceed to send? (y/n)
---
Example 2: Markdown Content with Default Sender
User: 发一封邮件给 manager@company.com,主题是"AR眼镜行业分析",内容是 [用户提供的 Markdown 报告]
Skill: 我来帮您发送这封邮件。检测到您没有指定发件人邮箱,我将使用默认邮箱 agent_skill_test@126.com 发送。
同时,我发现您的内容是 Markdown 格式,将自动转换为美观的 HTML 格式,让报告更易读。
为了使用 126 邮箱发送,我需要您提供 SMTP 授权码。
获取授权码步骤: 1. 登录 126 邮箱 → 设置 → POP3/SMTP/IMAP 2. 启用 "IMAP/SMTP 服务" 3. 复制显示的授权密码
请提供 agent_skill_test@126.com 的授权码:
User: [提供授权码]
Skill: 准备发送邮件:
- 发件人: agent_skill_test@126.com
- 收件人: manager@company.com
- 主题: AR眼镜行业分析
- 格式: Markdown → HTML (自动转换)
- 内容预览: 包含标题、列表、表格等格式化内容
确认发送? (y/n)
---
Example 3: Default Sender with Missing Content
User: 发邮件给 user@company.com,主题是项目报告
Skill: 我需要收集邮件内容:
邮件正文是什么?
User: 项目已完成,所有里程碑都已达成。
Skill: 我将使用默认邮箱 agent_skill_test@126.com 发送。
为了使用 126 邮箱发送,我需要您提供 SMTP 授权码。
获取授权码步骤: 1. 登录 126 邮箱 → 设置 → POP3/SMTP/IMAP 2. 启用 "IMAP/SMTP 服务" 3. 复制显示的授权密码
请提供 agent_skill_test@126.com 的授权码:
User: [提供授权码]
Skill: 准备发送邮件:
- 发件人: agent_skill_test@126.com
- 收件人: user@company.com
- 主题: 项目报告
- 内容: 项目已完成,所有里程碑都已达成。
确认发送?(y/n)
---
Example 4: User Specifies Custom Sender Email
User: Send a report to boss@company.com from myname@outlook.com about Q4 results, with attachment /home/user/q4_report.pdf
Skill: I'll help you send that email. Please provide the email content:
What should the email say about the Q4 results?
User: Please find attached the Q4 financial report. Let me know if you have any questions.
Skill: I see you're using Outlook. You'll need your Outlook account password (same as web login).
Please provide your Outlook password:
User: mypassword123
Skill: Ready to send:
- From: myname@outlook.com
- To: boss@company.com
- Subject: Q4 results
- Content: Please find attached the Q4 financial report. Let me know if you have any questions.
- Attachment: /home/user/q4_report.pdf
Proceed to send? (y/n)
---
Example 5: Custom SMTP
User: Send email to test@domain.com from my@domain.com
Skill: I need to gather some information:
1. Subject: What is the email subject? 2. Content: What is the email body?
User: Subject is Test Email, content is Hello World
Skill: Since you're using a custom domain (my@domain.com), I'll need your SMTP configuration:
1. SMTP Server: What is your SMTP server address? 2. SMTP Port: What port should I use? 3. Username: What is your SMTP username? 4. Password: What is your SMTP password?
User: Server is smtp.mydomain.com, port 587, username is my@domain.com, password is secret
Skill: Ready to send:
- From: my@domain.com
- To: test@domain.com
- Subject: Test Email
- Content: Hello World
- SMTP Server: smtp.mydomain.com:587
Proceed to send? (y/n)
Script Command Reference
Once all information is collected, execute the script with appropriate parameters:
Basic Command
python3 scripts/send_email.py \
--to recipient@example.com \
--subject "Email Subject" \
--content "Email body content" \
--smtp-server smtp.gmail.com \
--smtp-port 587 \
--username your@email.com \
--password your-password-or-app-passwordHTML Email
python3 scripts/send_email.py \
--to recipient@example.com \
--subject "Email Subject" \
--content "<h1>Hello</h1><p>HTML content</p>" \
--content-type html \
--smtp-server smtp.gmail.com \
--smtp-port 587 \
--username your@email.com \
--password your-passwordEmail with Attachments
python3 scripts/send_email.py \
--to recipient@example.com \
--subject "Email Subject" \
--content "Email body" \
--attach /path/to/file1.pdf \
--attach /path/to/file2.csv \
--smtp-server smtp.gmail.com \
--smtp-port 587 \
--username your@email.com \
--password your-passwordEmail with Template
python3 scripts/send_email.py \
--to recipient@example.com \
--subject "Email Subject" \
--template assets/simple-notification.html \
--template-vars '{"title":"Notification","message":"Your report is ready!"}' \
--content-type html \
--smtp-server smtp.gmail.com \
--smtp-port 587 \
--username your@email.com \
--password your-passwordSSL Connection (instead of TLS)
python3 scripts/send_email.py \
--to recipient@example.com \
--subject "Email Subject" \
--content "Email body" \
--smtp-server smtp.gmail.com \
--smtp-port 465 \
--use-ssl \
--username your@email.com \
--password your-passwordSMTP Server Configuration Reference
| Provider | SMTP Server | Port | Encryption | Password Type | Username |
|---|---|---|---|---|---|
| Gmail | smtp.gmail.com | 587 | TLS | App Password | Full email |
| Gmail | smtp.gmail.com | 465 | SSL | App Password | Full email |
| Outlook | smtp.office365.com | 587 | TLS | Account Password | Full email |
| QQ Mail | smtp.qq.com | 587 | TLS | Authorization Code | Full email |
| QQ Mail | smtp.qq.com | 465 | SSL | Authorization Code | Full email |
| 163 Mail | smtp.163.com | 465 | SSL | Authorization Code | Full email |
| 163 Mail | smtp.163.com | 994 | SSL | Authorization Code | Full email |
| 126 Mail | smtp.126.com | 465 | SSL | Authorization Code | Full email |
| 126 Mail | smtp.126.com | 25 | SSL | Authorization Code | Full email |
| SendGrid | smtp.sendgrid.net | 587 | TLS | API Key | apikey |
| SendGrid | smtp.sendgrid.net | 465 | SSL | API Key | apikey |
| Mailgun | smtp.mailgun.org | 587 | TLS | SMTP Password | From dashboard |
Installation
Before using this skill, install the required Python dependencies:
pip install -r requirements.txtThis installs the markdown library (version ≥3.4.0) which is required for automatic Markdown to HTML conversion.
Resources
scripts/send_email.py
Main Python script for sending emails. Supports:
- Plain text and HTML content
- Multiple file attachments
- Email templates with variable substitution
- TLS and SSL encryption
- Custom sender names
references/smtp-servers.md
Common SMTP server configurations including Gmail, Outlook, 126, QQ, 163, SendGrid, Mailgun, and Aliyun.
assets/
Email templates:
simple-notification.html- Basic notification templatereport-summary.html- Professional report template with metrics grid
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; background: #f5f5f5; }
.container { max-width: 650px; margin: 30px auto; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
.header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; }
.header h1 { margin: 0; font-size: 24px; }
.header .date { margin-top: 5px; opacity: 0.9; font-size: 14px; }
.content { padding: 30px; }
.section { margin-bottom: 25px; }
.section h3 { color: #667eea; margin-bottom: 15px; border-bottom: 2px solid #f0f0f0; padding-bottom: 8px; }
.stat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 20px; }
.stat { background: #f8f9fa; padding: 15px; border-radius: 6px; text-align: center; }
.stat-value { font-size: 28px; font-weight: bold; color: #667eea; }
.stat-label { font-size: 12px; color: #666; text-transform: uppercase; margin-top: 5px; }
.list-item { padding: 10px 0; border-bottom: 1px solid #f0f0f0; }
.list-item:last-child { border-bottom: none; }
.footer { background: #f8f9fa; padding: 20px; text-align: center; font-size: 12px; color: #888; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>{{report_title}}</h1>
<div class="date">{{date}}</div>
</div>
<div class="content">
<div class="section">
<h3>Summary</h3>
<p>{{summary}}</p>
</div>
<div class="section">
<h3>Key Metrics</h3>
<div class="stat-grid">
<div class="stat">
<div class="stat-value">{{metric1_value}}</div>
<div class="stat-label">{{metric1_label}}</div>
</div>
<div class="stat">
<div class="stat-value">{{metric2_value}}</div>
<div class="stat-label">{{metric2_label}}</div>
</div>
</div>
</div>
<div class="section">
<h3>Highlights</h3>
{{highlights}}
</div>
</div>
<div class="footer">
This report was generated automatically. Contact {{contact_email}} for questions.
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { border-bottom: 2px solid #007bff; padding-bottom: 10px; margin-bottom: 20px; }
.content { margin-bottom: 20px; }
.footer { border-top: 1px solid #eee; padding-top: 10px; font-size: 12px; color: #666; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h2>{{title}}</h2>
</div>
<div class="content">
<p>{{message}}</p>
</div>
<div class="footer">
<p>This is an automated notification.</p>
</div>
</div>
</body>
</html>
Common SMTP Server Configurations
Gmail
- Server:
smtp.gmail.com - Port:
587(TLS) or465(SSL) - Requires App Password: https://myaccount.google.com/apppasswords
- Use:
--use-tlsfor port 587,--use-sslfor port 465
Outlook/Office 365
- Server:
smtp.office365.com - Port:
587(TLS) - No special setup required for standard accounts
QQ Mail
- Server:
smtp.qq.com - Port:
587(TLS) or465(SSL) - Requires SMTP authorization code in account settings
126 Mail
- Server:
smtp.126.com - Port:
465(SSL) or25(SSL) - Requires SMTP authorization code (not login password)
- Get authorization code: Login → Settings → POP3/SMTP/IMAP → Enable SMTP
163 Mail
- Server:
smtp.163.com - Port:
465(SSL) or994(SSL) - Requires SMTP authorization code (not login password)
SendGrid
- Server:
smtp.sendgrid.net - Port:
587(TLS) or465(SSL) - Use API key as password
Mailgun
- Server:
smtp.mailgun.org - Port:
587(TLS) - Use SMTP credentials from Mailgun dashboard
Aliyun
- Server:
smtp.aliyun.com - Port:
465(SSL) - Requires SMTP password in account settings
# Email sending dependencies
markdown>=3.4.0
#!/usr/bin/env python3
"""Email sending utility using smtplib with support for HTML and attachments."""
import argparse
import email.message
import email.policy
import json
import mimetypes
import os
import re
import smtplib
import ssl
import sys
from email.utils import formataddr
from pathlib import Path
from typing import Optional
import markdown
def is_markdown_content(content: str) -> bool:
"""Detect if content is in markdown format.
Args:
content: Text content to check
Returns:
True if content appears to be markdown
"""
# Check for common markdown patterns
markdown_patterns = [
r'^#{1,6}\s+.+$', # Headers
r'^\*{1,3}.+\*{1,3}$', # Bold/italic
r'^\s*[-*+]\s+', # Unordered lists
r'^\s*\d+\.\s+', # Ordered lists
r'^\s*>\s+', # Blockquotes
r'```', # Code blocks
r'\[.+\]\(.+\)', # Links
r'^\s*---+\s*$', # Horizontal rules
r'^\*\*.+\*\*', # Bold
]
lines = content.split('\n')
markdown_score = 0
for line in lines:
for pattern in markdown_patterns:
if re.match(pattern, line.strip(), re.MULTILINE):
markdown_score += 1
break
# If more than 20% of lines have markdown syntax, consider it markdown
return markdown_score > len(lines) * 0.2 or markdown_score > 3
def markdown_to_html(content: str) -> str:
"""Convert markdown content to styled HTML.
Args:
content: Markdown text
Returns:
HTML formatted content with styling
"""
# Use markdown library with extensions
html = markdown.markdown(
content,
extensions=['extra', 'nl2br', 'sane_lists', 'tables']
)
# Wrap with email-friendly styling
styled_html = f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}}
.email-content {{
background-color: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}}
h1 {{
color: #2c3e50;
border-bottom: 3px solid #3498db;
padding-bottom: 10px;
margin-top: 0;
}}
h2 {{
color: #34495e;
border-bottom: 2px solid #95a5a6;
padding-bottom: 8px;
margin-top: 30px;
}}
h3 {{
color: #5d6d7e;
margin-top: 25px;
}}
h4 {{
color: #7f8c8d;
}}
p {{
margin: 12px 0;
}}
ul, ol {{
margin: 12px 0;
padding-left: 30px;
}}
li {{
margin: 8px 0;
}}
code {{
background-color: #f8f9fa;
padding: 2px 6px;
border-radius: 3px;
font-family: 'Courier New', monospace;
font-size: 0.9em;
color: #e74c3c;
}}
pre {{
background-color: #f8f9fa;
padding: 15px;
border-radius: 5px;
border-left: 4px solid #3498db;
overflow-x: auto;
}}
pre code {{
background-color: transparent;
padding: 0;
color: inherit;
}}
blockquote {{
border-left: 4px solid #3498db;
padding-left: 20px;
margin: 15px 0;
color: #555;
font-style: italic;
}}
strong {{
color: #2c3e50;
font-weight: 600;
}}
em {{
color: #34495e;
}}
hr {{
border: none;
border-top: 2px solid #ecf0f1;
margin: 30px 0;
}}
table {{
border-collapse: collapse;
width: 100%;
margin: 20px 0;
}}
th, td {{
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}}
th {{
background-color: #3498db;
color: white;
font-weight: 600;
}}
tr:nth-child(even) {{
background-color: #f8f9fa;
}}
a {{
color: #3498db;
text-decoration: none;
}}
a:hover {{
text-decoration: underline;
}}
</style>
</head>
<body>
<div class="email-content">
{html}
</div>
</body>
</html>
"""
return styled_html
def read_template(template_path: str, variables: dict) -> str:
"""Read email template and substitute variables.
Template variables should be in format {{variable_name}}.
Args:
template_path: Path to template file
variables: Dictionary of variable substitutions
Returns:
Rendered email content
"""
with open(template_path, 'r', encoding='utf-8') as f:
content = f.read()
for key, value in variables.items():
content = content.replace('{{' + key + '}}', str(value))
return content
def send_email(
to: str,
subject: str,
content: str,
smtp_server: str,
smtp_port: int,
username: str,
password: str,
from_addr: Optional[str] = None,
from_name: Optional[str] = None,
content_type: str = 'html',
attachments: Optional[list[str]] = None,
use_tls: bool = True,
use_ssl: bool = False,
) -> bool:
"""Send email via SMTP.
Args:
to: Recipient email address
subject: Email subject
content: Email body content
smtp_server: SMTP server hostname
smtp_port: SMTP server port
username: SMTP username
password: SMTP password
from_addr: Sender email address (defaults to username)
from_name: Sender display name
content_type: 'html' or 'plain'
attachments: List of file paths to attach
use_tls: Use STARTTLS encryption
use_ssl: Use SSL encryption (exclusive with use_tls)
Returns:
True if successful, False otherwise
"""
if from_addr is None:
from_addr = username
msg = email.message.EmailMessage(policy=email.policy.default)
msg['To'] = to
msg['Subject'] = subject
if from_name:
msg['From'] = formataddr((from_name, from_addr))
else:
msg['From'] = from_addr
if content_type == 'html':
msg.set_content(content, subtype='html')
else:
msg.set_content(content)
if attachments:
for filepath in attachments:
path = Path(filepath)
if not path.exists():
print(f"Warning: Attachment not found: {filepath}", file=sys.stderr)
continue
mime_type, encoding = mimetypes.guess_type(filepath)
if mime_type is None:
mime_type = 'application/octet-stream'
main_type, sub_type = mime_type.split('/', 1)
with open(filepath, 'rb') as f:
msg.add_attachment(
f.read(),
maintype=main_type,
subtype=sub_type,
filename=path.name,
)
try:
if use_ssl:
context = ssl.create_default_context()
with smtplib.SMTP_SSL(smtp_server, smtp_port, context=context) as server:
server.login(username, password)
server.send_message(msg)
else:
context = ssl.create_default_context()
with smtplib.SMTP(smtp_server, smtp_port) as server:
if use_tls:
server.starttls(context=context)
server.login(username, password)
server.send_message(msg)
print(f"Email sent successfully to {to}")
return True
except Exception as e:
print(f"Failed to send email: {e}", file=sys.stderr)
return False
def main():
parser = argparse.ArgumentParser(description='Send email via SMTP')
parser.add_argument('--to', required=True, help='Recipient email address')
parser.add_argument('--subject', required=True, help='Email subject')
parser.add_argument('--content', help='Email body content (or use --template)')
parser.add_argument('--template', help='Path to email template file')
parser.add_argument('--template-vars', help='JSON string of template variables')
parser.add_argument('--smtp-server', required=True, help='SMTP server hostname')
parser.add_argument('--smtp-port', type=int, required=True, help='SMTP server port')
parser.add_argument('--username', required=True, help='SMTP username')
parser.add_argument('--password', required=True, help='SMTP password')
parser.add_argument('--from-addr', help='Sender email address (default: username)')
parser.add_argument('--from-name', help='Sender display name')
parser.add_argument('--content-type', choices=['plain', 'html'], default='plain',
help='Content type (default: plain)')
parser.add_argument('--attach', action='append', help='File attachment (can be used multiple times)')
parser.add_argument('--no-tls', action='store_true', help='Disable STARTTLS')
parser.add_argument('--use-ssl', action='store_true', help='Use SSL instead of STARTTLS')
args = parser.parse_args()
if args.template and args.content:
print("Error: Cannot specify both --template and --content", file=sys.stderr)
sys.exit(1)
if args.template:
if not args.template_vars:
print("Warning: No template variables provided", file=sys.stderr)
variables = {}
else:
variables = json.loads(args.template_vars)
content = read_template(args.template, variables)
elif args.content:
content = args.content
else:
content = ""
# Auto-detect and convert markdown to HTML
content_type = args.content_type
if content and content_type == 'plain' and is_markdown_content(content):
print("Detected markdown content, converting to HTML...", file=sys.stderr)
content = markdown_to_html(content)
content_type = 'html'
success = send_email(
to=args.to,
subject=args.subject,
content=content,
smtp_server=args.smtp_server,
smtp_port=args.smtp_port,
username=args.username,
password=args.password,
from_addr=args.from_addr,
from_name=args.from_name,
content_type=content_type,
attachments=args.attach,
use_tls=not args.no_tls,
use_ssl=args.use_ssl,
)
sys.exit(0 if success else 1)
if __name__ == '__main__':
main()
Related skills
How it compares
Pick send-email over API-based email MCPs when you need direct SMTP scripting with Markdown, templates, and attachments from a local Python helper.
FAQ
Which email providers does send-email support?
send-email supports Gmail, Outlook, QQ Mail, 163/126 Mail, and custom SMTP servers. The skill prompts for provider-specific credentials such as Gmail app passwords or QQ authorization codes before calling scripts/send_email.py.
How does send-email handle Markdown message bodies?
send-email auto-detects Markdown content and converts it to styled HTML before sending. Developers can write bodies in Markdown without passing a separate --content-type html flag for basic formatted messages.
What script does send-email execute to deliver mail?
send-email executes python3 scripts/send_email.py with recipient, subject, content or template flags, SMTP server settings, and optional --attach paths. The script supports TLS, SSL, custom sender names, and template variable JSON.