Add tool call logging and reply fields
All checks were successful
Build And Test / publish (push) Successful in 49s
All checks were successful
Build And Test / publish (push) Successful in 49s
This commit is contained in:
@@ -2,18 +2,21 @@ from typing import Optional
|
||||
from fastmcp import FastMCP
|
||||
|
||||
from services.email_service import EmailService
|
||||
from tools.logging_utils import log_tool_call
|
||||
|
||||
|
||||
def register_email_tools(mcp: FastMCP, service: EmailService):
|
||||
"""Register all email-related MCP tools."""
|
||||
|
||||
@mcp.tool(description="List all mailboxes/folders in the email account. Returns name, path, message count, and unread count for each mailbox.")
|
||||
@log_tool_call
|
||||
def list_mailboxes() -> list[dict]:
|
||||
"""List all IMAP mailboxes/folders."""
|
||||
mailboxes = service.list_mailboxes()
|
||||
return [m.model_dump() for m in mailboxes]
|
||||
|
||||
@mcp.tool(description="List emails in a mailbox with pagination. Returns email summaries including subject, from, date, and read status.")
|
||||
@log_tool_call
|
||||
def list_emails(
|
||||
mailbox: str = "INBOX",
|
||||
limit: int = 50,
|
||||
@@ -33,6 +36,7 @@ def register_email_tools(mcp: FastMCP, service: EmailService):
|
||||
return result.model_dump()
|
||||
|
||||
@mcp.tool(description="List draft emails in the Drafts mailbox with pagination.")
|
||||
@log_tool_call
|
||||
def list_drafts(
|
||||
mailbox: Optional[str] = None,
|
||||
limit: int = 50,
|
||||
@@ -52,6 +56,7 @@ def register_email_tools(mcp: FastMCP, service: EmailService):
|
||||
return result.model_dump()
|
||||
|
||||
@mcp.tool(description="Read a specific email by ID with full body content and attachment information.")
|
||||
@log_tool_call
|
||||
def read_email(
|
||||
mailbox: str,
|
||||
email_id: str,
|
||||
@@ -69,6 +74,7 @@ def register_email_tools(mcp: FastMCP, service: EmailService):
|
||||
return result.model_dump() if result else None
|
||||
|
||||
@mcp.tool(description="Search emails in a mailbox using various criteria like subject, sender, or body content.")
|
||||
@log_tool_call
|
||||
def search_emails(
|
||||
query: str,
|
||||
mailbox: str = "INBOX",
|
||||
@@ -94,6 +100,7 @@ def register_email_tools(mcp: FastMCP, service: EmailService):
|
||||
return result.model_dump()
|
||||
|
||||
@mcp.tool(description="Move an email from one mailbox/folder to another.")
|
||||
@log_tool_call
|
||||
def move_email(
|
||||
email_id: str,
|
||||
source_mailbox: str,
|
||||
@@ -111,6 +118,7 @@ def register_email_tools(mcp: FastMCP, service: EmailService):
|
||||
return result.model_dump()
|
||||
|
||||
@mcp.tool(description="Delete an email, either moving it to trash or permanently deleting it.")
|
||||
@log_tool_call
|
||||
def delete_email(
|
||||
email_id: str,
|
||||
mailbox: str,
|
||||
@@ -128,6 +136,7 @@ def register_email_tools(mcp: FastMCP, service: EmailService):
|
||||
return result.model_dump()
|
||||
|
||||
@mcp.tool(description="Delete a drafted email by ID, optionally permanently.")
|
||||
@log_tool_call
|
||||
def delete_draft(
|
||||
email_id: str,
|
||||
mailbox: Optional[str] = None,
|
||||
@@ -145,33 +154,53 @@ def register_email_tools(mcp: FastMCP, service: EmailService):
|
||||
return result.model_dump()
|
||||
|
||||
@mcp.tool(description="Save a new draft email to the Drafts mailbox.")
|
||||
@log_tool_call
|
||||
def save_draft(
|
||||
to: list[str],
|
||||
subject: str,
|
||||
body: str,
|
||||
to: Optional[list[str]] = None,
|
||||
subject: Optional[str] = None,
|
||||
body: Optional[str] = None,
|
||||
cc: Optional[list[str]] = None,
|
||||
bcc: Optional[list[str]] = None,
|
||||
reply_to: Optional[str] = None,
|
||||
html_body: Optional[str] = None,
|
||||
mailbox: Optional[str] = None,
|
||||
reply_to_email_id: Optional[str] = None,
|
||||
reply_mailbox: Optional[str] = None,
|
||||
reply_all: bool = False,
|
||||
) -> dict:
|
||||
"""
|
||||
Save a new email draft.
|
||||
|
||||
Args:
|
||||
to: List of recipient email addresses
|
||||
subject: Email subject line
|
||||
body: Plain text email body
|
||||
to: List of recipient email addresses (required unless reply_to_email_id is set)
|
||||
subject: Email subject line (required unless reply_to_email_id is set)
|
||||
body: Plain text email body (required unless reply_to_email_id is set)
|
||||
cc: List of CC recipients (optional)
|
||||
bcc: List of BCC recipients (optional)
|
||||
reply_to: Reply-to address (optional)
|
||||
html_body: HTML version of the email body (optional)
|
||||
mailbox: Drafts mailbox/folder override (default: auto-detect)
|
||||
reply_to_email_id: Email ID to reply to (optional)
|
||||
reply_mailbox: Mailbox containing the reply_to_email_id (default: INBOX)
|
||||
reply_all: Whether to include original recipients when replying (default: False)
|
||||
"""
|
||||
result = service.save_draft(to, subject, body, cc, bcc, reply_to, html_body, mailbox)
|
||||
result = service.save_draft(
|
||||
to,
|
||||
subject,
|
||||
body,
|
||||
cc,
|
||||
bcc,
|
||||
reply_to,
|
||||
html_body,
|
||||
mailbox,
|
||||
reply_to_email_id,
|
||||
reply_mailbox,
|
||||
reply_all,
|
||||
)
|
||||
return result.model_dump()
|
||||
|
||||
@mcp.tool(description="Edit an existing draft email. Only provided fields will be modified.")
|
||||
@log_tool_call
|
||||
def edit_draft(
|
||||
email_id: str,
|
||||
mailbox: Optional[str] = None,
|
||||
@@ -182,6 +211,9 @@ def register_email_tools(mcp: FastMCP, service: EmailService):
|
||||
bcc: Optional[list[str]] = None,
|
||||
reply_to: Optional[str] = None,
|
||||
html_body: Optional[str] = None,
|
||||
reply_to_email_id: Optional[str] = None,
|
||||
reply_mailbox: Optional[str] = None,
|
||||
reply_all: bool = False,
|
||||
) -> dict:
|
||||
"""
|
||||
Update an existing draft email.
|
||||
@@ -196,6 +228,9 @@ def register_email_tools(mcp: FastMCP, service: EmailService):
|
||||
bcc: List of BCC recipients (optional)
|
||||
reply_to: Reply-to address (optional)
|
||||
html_body: HTML version of the email body (optional)
|
||||
reply_to_email_id: Email ID to reply to (optional)
|
||||
reply_mailbox: Mailbox containing the reply_to_email_id (default: INBOX)
|
||||
reply_all: Whether to include original recipients when replying (default: False)
|
||||
"""
|
||||
result = service.update_draft(
|
||||
email_id,
|
||||
@@ -207,34 +242,44 @@ def register_email_tools(mcp: FastMCP, service: EmailService):
|
||||
bcc,
|
||||
reply_to,
|
||||
html_body,
|
||||
reply_to_email_id,
|
||||
reply_mailbox,
|
||||
reply_all,
|
||||
)
|
||||
return result.model_dump()
|
||||
|
||||
@mcp.tool(description="Send a new email via SMTP. Supports plain text and HTML content, CC, BCC, reply-to, and custom sender.")
|
||||
@log_tool_call
|
||||
async def send_email(
|
||||
to: list[str],
|
||||
subject: str,
|
||||
body: str,
|
||||
to: Optional[list[str]] = None,
|
||||
subject: Optional[str] = None,
|
||||
body: Optional[str] = None,
|
||||
cc: Optional[list[str]] = None,
|
||||
bcc: Optional[list[str]] = None,
|
||||
reply_to: Optional[str] = None,
|
||||
html_body: Optional[str] = None,
|
||||
sender_email: Optional[str] = None,
|
||||
sender_name: Optional[str] = None,
|
||||
reply_to_email_id: Optional[str] = None,
|
||||
reply_mailbox: Optional[str] = None,
|
||||
reply_all: bool = False,
|
||||
) -> dict:
|
||||
"""
|
||||
Send a new email.
|
||||
|
||||
Args:
|
||||
to: List of recipient email addresses
|
||||
subject: Email subject line
|
||||
body: Plain text email body
|
||||
to: List of recipient email addresses (required unless reply_to_email_id is set)
|
||||
subject: Email subject line (required unless reply_to_email_id is set)
|
||||
body: Plain text email body (required unless reply_to_email_id is set)
|
||||
cc: List of CC recipients (optional)
|
||||
bcc: List of BCC recipients (optional)
|
||||
reply_to: Reply-to address (optional)
|
||||
html_body: HTML version of the email body (optional)
|
||||
sender_email: Sender email address (optional, defaults to SMTP_FROM_EMAIL)
|
||||
sender_name: Sender display name (optional, defaults to SMTP_FROM_NAME)
|
||||
reply_to_email_id: Email ID to reply to (optional)
|
||||
reply_mailbox: Mailbox containing the reply_to_email_id (default: INBOX)
|
||||
reply_all: Whether to include original recipients when replying (default: False)
|
||||
"""
|
||||
result = await service.send_email(
|
||||
to,
|
||||
@@ -246,52 +291,16 @@ def register_email_tools(mcp: FastMCP, service: EmailService):
|
||||
html_body,
|
||||
sender_email,
|
||||
sender_name,
|
||||
)
|
||||
return result.model_dump()
|
||||
|
||||
@mcp.tool(description="Reply to an existing email by ID, with optional reply-all behavior.")
|
||||
async def reply_email(
|
||||
email_id: str,
|
||||
body: str,
|
||||
mailbox: str = "INBOX",
|
||||
reply_all: bool = False,
|
||||
cc: Optional[list[str]] = None,
|
||||
bcc: Optional[list[str]] = None,
|
||||
reply_to: Optional[str] = None,
|
||||
html_body: Optional[str] = None,
|
||||
sender_email: Optional[str] = None,
|
||||
sender_name: Optional[str] = None,
|
||||
) -> dict:
|
||||
"""
|
||||
Reply to an existing email.
|
||||
|
||||
Args:
|
||||
email_id: The unique ID of the email to reply to
|
||||
body: Plain text email body
|
||||
mailbox: The mailbox containing the email (default: INBOX)
|
||||
reply_all: Whether to include original recipients (default: False)
|
||||
cc: List of CC recipients (optional)
|
||||
bcc: List of BCC recipients (optional)
|
||||
reply_to: Reply-to address for the reply (optional)
|
||||
html_body: HTML version of the email body (optional)
|
||||
sender_email: Sender email address (optional, defaults to SMTP_FROM_EMAIL)
|
||||
sender_name: Sender display name (optional, defaults to SMTP_FROM_NAME)
|
||||
"""
|
||||
result = await service.reply_email(
|
||||
mailbox,
|
||||
email_id,
|
||||
body,
|
||||
None,
|
||||
None,
|
||||
reply_to_email_id,
|
||||
reply_mailbox,
|
||||
reply_all,
|
||||
cc,
|
||||
bcc,
|
||||
reply_to,
|
||||
html_body,
|
||||
sender_email,
|
||||
sender_name,
|
||||
)
|
||||
return result.model_dump()
|
||||
|
||||
@mcp.tool(description="Set or remove IMAP flags on an email. Standard flags: \\Seen, \\Answered, \\Flagged, \\Deleted, \\Draft. Custom keywords are also supported.")
|
||||
@log_tool_call
|
||||
def set_email_flags(
|
||||
email_id: str,
|
||||
mailbox: str,
|
||||
@@ -311,6 +320,7 @@ def register_email_tools(mcp: FastMCP, service: EmailService):
|
||||
return result.model_dump()
|
||||
|
||||
@mcp.tool(description="Unsubscribe from a mailing list. Parses List-Unsubscribe headers and attempts automatic unsubscribe via HTTP or provides mailto instructions.")
|
||||
@log_tool_call
|
||||
async def unsubscribe_email(
|
||||
email_id: str,
|
||||
mailbox: str = "INBOX",
|
||||
|
||||
Reference in New Issue
Block a user