Skip to content

Commit a267247

Browse files
authored
fix: Use since for search and add some description (#3)
* fix: Use since for search and add some description * Support address
1 parent eb4e3f9 commit a267247

File tree

5 files changed

+99
-50
lines changed

5 files changed

+99
-50
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,4 @@ cython_debug/
140140
#.idea/
141141

142142
tests/config.toml
143+
local/

mcp_email_server/app.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,32 @@ async def add_email_account(email: EmailSettings) -> None:
3333
settings.store()
3434

3535

36-
@mcp.tool()
36+
@mcp.tool(description="Paginate emails, page start at 1, before and since as UTC datetime.")
3737
async def page_email(
3838
account_name: str,
3939
page: int = 1,
4040
page_size: int = 10,
4141
before: datetime | None = None,
42-
after: datetime | None = None,
43-
include: str | None = None,
42+
since: datetime | None = None,
43+
subject: str | None = None,
44+
body: str | None = None,
45+
text: str | None = None,
46+
from_address: str | None = None,
47+
to_address: str | None = None,
4448
) -> EmailPageResponse:
4549
handler = dispatch_handler(account_name)
4650

47-
return await handler.get_emails(page=page, page_size=page_size, before=before, after=after, include=include)
51+
return await handler.get_emails(
52+
page=page,
53+
page_size=page_size,
54+
before=before,
55+
since=since,
56+
subject=subject,
57+
body=body,
58+
text=text,
59+
from_address=from_address,
60+
to_address=to_address,
61+
)
4862

4963

5064
@mcp.tool()

mcp_email_server/emails/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ async def get_emails(
1414
page_size: int = 10,
1515
before: datetime | None = None,
1616
after: datetime | None = None,
17-
include: str | None = None,
17+
subject: str | None = None,
18+
body: str | None = None,
19+
text: str | None = None,
20+
from_address: str | None = None,
21+
to_address: str | None = None,
1822
) -> "EmailPageResponse":
1923
"""
2024
Get emails

mcp_email_server/emails/classic.py

Lines changed: 71 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from mcp_email_server.config import EmailServer, EmailSettings
1313
from mcp_email_server.emails import EmailHandler
1414
from mcp_email_server.emails.models import EmailData, EmailPageResponse
15+
from mcp_email_server.log import logger
1516

1617

1718
class EmailClient:
@@ -82,13 +83,17 @@ def _parse_email_data(self, raw_email: bytes) -> dict[str, Any]: # noqa: C901
8283
"attachments": attachments,
8384
}
8485

85-
async def get_emails_stream( # noqa: C901
86+
async def get_emails_stream(
8687
self,
8788
page: int = 1,
8889
page_size: int = 10,
8990
before: datetime | None = None,
90-
after: datetime | None = None,
91-
include: str | None = None,
91+
since: datetime | None = None,
92+
subject: str | None = None,
93+
body: str | None = None,
94+
text: str | None = None,
95+
from_address: str | None = None,
96+
to_address: str | None = None,
9297
) -> AsyncGenerator[dict[str, Any], None]:
9398
imap = self.imap_class(self.email_server.host, self.email_server.port)
9499
try:
@@ -100,22 +105,13 @@ async def get_emails_stream( # noqa: C901
100105
await imap.login(self.email_server.user_name, self.email_server.password)
101106
await imap.select("INBOX")
102107

103-
# Build search criteria
104-
search_criteria = []
105-
if before:
106-
search_criteria.extend(["BEFORE", before.isoformat()])
107-
if after:
108-
search_criteria.extend(["AFTER", after.isoformat()])
109-
if include:
110-
search_criteria.extend(["TEXT", include])
111-
112-
# If no specific criteria, search for ALL
113-
if not search_criteria:
114-
search_criteria = ["ALL"]
115-
108+
search_criteria = self._build_search_criteria(before, since, subject, body, text, from_address, to_address)
116109
# Search for messages
117110
_, messages = await imap.search(*search_criteria)
111+
logger.info(f"Get: Search criteria: {search_criteria}")
112+
logger.debug(f"Raw messages: {messages}")
118113
message_ids = messages[0].split()
114+
logger.debug(f"Message IDs: {message_ids}")
119115
start = (page - 1) * page_size
120116
end = start + page_size
121117

@@ -151,23 +147,59 @@ async def get_emails_stream( # noqa: C901
151147
yield parsed_email
152148
except Exception as e:
153149
# Log error but continue with other emails
154-
print(f"Error parsing email: {e!s}")
150+
logger.error(f"Error parsing email: {e!s}")
155151
else:
156-
print(f"Could not find email data in response for message ID: {message_id_str}")
152+
logger.error(f"Could not find email data in response for message ID: {message_id_str}")
157153
except Exception as e:
158-
print(f"Error fetching message {message_id}: {e!s}")
154+
logger.error(f"Error fetching message {message_id}: {e!s}")
159155
finally:
160156
# Ensure we logout properly
161157
try:
162158
await imap.logout()
163159
except Exception as e:
164-
print(f"Error during logout: {e}")
160+
logger.info(f"Error during logout: {e}")
161+
162+
@staticmethod
163+
def _build_search_criteria(
164+
before: datetime | None = None,
165+
since: datetime | None = None,
166+
subject: str | None = None,
167+
body: str | None = None,
168+
text: str | None = None,
169+
from_address: str | None = None,
170+
to_address: str | None = None,
171+
):
172+
search_criteria = []
173+
if before:
174+
search_criteria.extend(["BEFORE", before.strftime("%d-%b-%Y").upper()])
175+
if since:
176+
search_criteria.extend(["SINCE", since.strftime("%d-%b-%Y").upper()])
177+
if subject:
178+
search_criteria.extend(["SUBJECT", subject])
179+
if body:
180+
search_criteria.extend(["BODY", body])
181+
if text:
182+
search_criteria.extend(["TEXT", text])
183+
if from_address:
184+
search_criteria.extend(["FROM", from_address])
185+
if to_address:
186+
search_criteria.extend(["TO", to_address])
187+
188+
# If no specific criteria, search for ALL
189+
if not search_criteria:
190+
search_criteria = ["ALL"]
191+
192+
return search_criteria
165193

166194
async def get_email_count(
167195
self,
168196
before: datetime | None = None,
169-
after: datetime | None = None,
170-
include: str | None = None,
197+
since: datetime | None = None,
198+
subject: str | None = None,
199+
body: str | None = None,
200+
text: str | None = None,
201+
from_address: str | None = None,
202+
to_address: str | None = None,
171203
) -> int:
172204
imap = self.imap_class(self.email_server.host, self.email_server.port)
173205
try:
@@ -178,20 +210,8 @@ async def get_email_count(
178210
# Login and select inbox
179211
await imap.login(self.email_server.user_name, self.email_server.password)
180212
await imap.select("INBOX")
181-
182-
# Build search criteria
183-
search_criteria = []
184-
if before:
185-
search_criteria.extend(["BEFORE", before.isoformat()])
186-
if after:
187-
search_criteria.extend(["AFTER", after.isoformat()])
188-
if include:
189-
search_criteria.extend(["TEXT", include])
190-
191-
# If no specific criteria, search for ALL
192-
if not search_criteria:
193-
search_criteria = ["ALL"]
194-
213+
search_criteria = self._build_search_criteria(before, since, subject, body, text, from_address, to_address)
214+
logger.info(f"Count: Search criteria: {search_criteria}")
195215
# Search for messages and count them
196216
_, messages = await imap.search(*search_criteria)
197217
return len(messages[0].split())
@@ -200,7 +220,7 @@ async def get_email_count(
200220
try:
201221
await imap.logout()
202222
except Exception as e:
203-
print(f"Error during logout: {e}")
223+
logger.info(f"Error during logout: {e}")
204224

205225
async def send_email(self, recipient: str, subject: str, body: str):
206226
msg = MIMEText(body)
@@ -232,19 +252,27 @@ async def get_emails(
232252
page: int = 1,
233253
page_size: int = 10,
234254
before: datetime | None = None,
235-
after: datetime | None = None,
236-
include: str | None = None,
255+
since: datetime | None = None,
256+
subject: str | None = None,
257+
body: str | None = None,
258+
text: str | None = None,
259+
from_address: str | None = None,
260+
to_address: str | None = None,
237261
) -> EmailPageResponse:
238262
emails = []
239-
async for email_data in self.incoming_client.get_emails_stream(page, page_size, before, after, include):
263+
async for email_data in self.incoming_client.get_emails_stream(
264+
page, page_size, before, since, subject, body, text, from_address, to_address
265+
):
240266
emails.append(EmailData.from_email(email_data))
241-
total = await self.incoming_client.get_email_count(before, after, include)
267+
total = await self.incoming_client.get_email_count(before, since, subject, body, text, from_address, to_address)
242268
return EmailPageResponse(
243269
page=page,
244270
page_size=page_size,
245271
before=before,
246-
after=after,
247-
include=include,
272+
since=since,
273+
subject=subject,
274+
body=body,
275+
text=text,
248276
emails=emails,
249277
total=total,
250278
)

mcp_email_server/emails/models.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ class EmailPageResponse(BaseModel):
2626
page: int
2727
page_size: int
2828
before: datetime | None
29-
after: datetime | None
30-
include: str | None
29+
since: datetime | None
30+
subject: str | None
31+
body: str | None
32+
text: str | None
3133
emails: list[EmailData]
3234
total: int

0 commit comments

Comments
 (0)