MCPcopy
hub / github.com/dataelement/Clawith / send_email

Function send_email

backend/app/services/email_service.py:156–254  ·  view source on GitHub ↗

Send an email via SMTP. Args: config: Resolved email config (from resolve_config) to: Recipient email address(es), comma-separated subject: Email subject body: Email body text cc: CC recipients, comma-separated attachments: List of workspace-relat

(
    config: dict,
    to: str,
    subject: str,
    body: str,
    cc: Optional[str] = None,
    attachments: Optional[list[str]] = None,
    workspace_path: Optional[Path] = None,
    agent_id: Optional[uuid.UUID] = None,
)

Source from the content-addressed store, hash-verified

154
155
156async def send_email(
157 config: dict,
158 to: str,
159 subject: str,
160 body: str,
161 cc: Optional[str] = None,
162 attachments: Optional[list[str]] = None,
163 workspace_path: Optional[Path] = None,
164 agent_id: Optional[uuid.UUID] = None,
165) -> str:
166 """Send an email via SMTP.
167
168 Args:
169 config: Resolved email config (from resolve_config)
170 to: Recipient email address(es), comma-separated
171 subject: Email subject
172 body: Email body text
173 cc: CC recipients, comma-separated
174 attachments: List of workspace-relative file paths to attach
175 workspace_path: Agent workspace root for resolving attachment paths
176 agent_id: Optional UUID of the agent for retrieving files from storage
177 """
178 cfg = resolve_config(config)
179 addr = cfg["email_address"]
180 password = cfg["auth_code"]
181
182 if not addr or not password:
183 return "❌ Email not configured. Please set email address and authorization code in tool config."
184
185 msg = MIMEMultipart()
186 msg["From"] = addr
187 msg["To"] = to
188 msg["Subject"] = subject
189 if cc:
190 msg["Cc"] = cc
191 msg["Message-ID"] = make_msgid()
192 msg["Date"] = datetime.now().strftime("%a, %d %b %Y %H:%M:%S %z")
193
194 msg.attach(MIMEText(body, "plain", "utf-8"))
195
196 # Attach files
197 if attachments and workspace_path:
198 from app.services.storage import get_storage_backend, normalize_storage_key
199 storage = get_storage_backend()
200
201 for rel_path in attachments:
202 clean_rel = rel_path.replace("\\", "/").strip().lstrip("/")
203 prefix = str(agent_id) if agent_id else workspace_path.name
204 storage_key = normalize_storage_key(f"{prefix}/{clean_rel}")
205 file_bytes = None
206 filename = Path(clean_rel).name
207
208 # 1. Try to read from the storage backend (e.g. S3 or local storage)
209 try:
210 if await storage.exists(storage_key) and await storage.is_file(storage_key):
211 file_bytes = await storage.read_bytes(storage_key)
212 except Exception:
213 pass

Callers 1

_handle_email_toolFunction · 0.90

Calls 8

get_storage_backendFunction · 0.90
normalize_storage_keyFunction · 0.90
send_smtp_emailFunction · 0.90
resolve_configFunction · 0.85
existsMethod · 0.45
is_fileMethod · 0.45
read_bytesMethod · 0.45
getMethod · 0.45

Tested by

no test coverage detected