w
This commit is contained in:
+79
-50
@@ -8,10 +8,10 @@ import json
|
||||
|
||||
from mcp.server.fastmcp import Context
|
||||
|
||||
from owa_mcp.server import mcp, AppContext
|
||||
from owa_mcp.owa_client import OWAClient, SessionExpiredError
|
||||
from owa_mcp.server import AppContext, mcp
|
||||
from owa_mcp.server_lifecycle import require_client
|
||||
from owa_mcp.utils import html_to_text, extract_links_from_html
|
||||
from owa_mcp.utils import extract_links_from_html, html_to_text
|
||||
|
||||
|
||||
def _get_client(ctx: Context) -> OWAClient:
|
||||
@@ -215,7 +215,12 @@ def _get_item_details(client: OWAClient, item_id: str) -> dict:
|
||||
item_type = item.get("__type", "")
|
||||
if any(
|
||||
t in item_type
|
||||
for t in ("MeetingRequest", "MeetingResponse", "MeetingCancellation", "CalendarItem")
|
||||
for t in (
|
||||
"MeetingRequest",
|
||||
"MeetingResponse",
|
||||
"MeetingCancellation",
|
||||
"CalendarItem",
|
||||
)
|
||||
):
|
||||
result["location"] = item.get(
|
||||
"Location",
|
||||
@@ -331,9 +336,7 @@ def get_emails(
|
||||
find_body = {
|
||||
"__type": "FindItemRequest:#Exchange",
|
||||
"ItemShape": item_shape,
|
||||
"ParentFolderIds": [
|
||||
{"__type": "FolderId:#Exchange", "Id": folder_id}
|
||||
],
|
||||
"ParentFolderIds": [{"__type": "FolderId:#Exchange", "Id": folder_id}],
|
||||
"Traversal": "Shallow",
|
||||
"Paging": {
|
||||
"__type": "IndexedPageView:#Exchange",
|
||||
@@ -392,18 +395,19 @@ def get_emails(
|
||||
|
||||
if not items:
|
||||
return json.dumps(
|
||||
{"item_ids": [], "count": 0} if ids_only
|
||||
else {"emails": [], "count": 0}
|
||||
{"item_ids": [], "count": 0} if ids_only else {"emails": [], "count": 0}
|
||||
)
|
||||
|
||||
if ids_only:
|
||||
result = []
|
||||
for item in items:
|
||||
result.append({
|
||||
"item_id": item.get("ItemId", {}).get("Id", ""),
|
||||
"date": item.get("DateTimeReceived", ""),
|
||||
"subject": item.get("Subject", ""),
|
||||
})
|
||||
result.append(
|
||||
{
|
||||
"item_id": item.get("ItemId", {}).get("Id", ""),
|
||||
"date": item.get("DateTimeReceived", ""),
|
||||
"subject": item.get("Subject", ""),
|
||||
}
|
||||
)
|
||||
return json.dumps({"item_ids": result, "count": len(result)})
|
||||
|
||||
emails = []
|
||||
@@ -763,9 +767,7 @@ def move_email(
|
||||
if not folder_id:
|
||||
return json.dumps({"error": f"Folder '{target_folder}' not found."})
|
||||
|
||||
items = [
|
||||
{"__type": "ItemId:#Exchange", "Id": iid} for iid in item_ids
|
||||
]
|
||||
items = [{"__type": "ItemId:#Exchange", "Id": iid} for iid in item_ids]
|
||||
|
||||
payload = {
|
||||
"__type": "MoveItemJsonRequest:#Exchange",
|
||||
@@ -824,9 +826,7 @@ def delete_email(
|
||||
try:
|
||||
client = _get_client(ctx)
|
||||
|
||||
items = [
|
||||
{"__type": "ItemId:#Exchange", "Id": iid} for iid in item_ids
|
||||
]
|
||||
items = [{"__type": "ItemId:#Exchange", "Id": iid} for iid in item_ids]
|
||||
|
||||
payload = {
|
||||
"__type": "DeleteItemJsonRequest:#Exchange",
|
||||
@@ -887,18 +887,31 @@ def download_attachments(
|
||||
attachments = details.get("attachments", [])
|
||||
|
||||
if not attachments:
|
||||
return json.dumps({"success": True, "downloaded": [], "count": 0,
|
||||
"message": "No attachments found."})
|
||||
return json.dumps(
|
||||
{
|
||||
"success": True,
|
||||
"downloaded": [],
|
||||
"count": 0,
|
||||
"message": "No attachments found.",
|
||||
}
|
||||
)
|
||||
|
||||
# Filter to non-inline file attachments with IDs
|
||||
file_attachments = [
|
||||
a for a in attachments
|
||||
a
|
||||
for a in attachments
|
||||
if a.get("attachment_id") and not a.get("is_inline", False)
|
||||
]
|
||||
|
||||
if not file_attachments:
|
||||
return json.dumps({"success": True, "downloaded": [], "count": 0,
|
||||
"message": "No downloadable file attachments."})
|
||||
return json.dumps(
|
||||
{
|
||||
"success": True,
|
||||
"downloaded": [],
|
||||
"count": 0,
|
||||
"message": "No downloadable file attachments.",
|
||||
}
|
||||
)
|
||||
|
||||
os.makedirs(target_folder, exist_ok=True)
|
||||
|
||||
@@ -938,17 +951,21 @@ def download_attachments(
|
||||
with open(filepath, "wb") as f:
|
||||
f.write(content)
|
||||
|
||||
downloaded.append({
|
||||
"name": filename,
|
||||
"path": filepath,
|
||||
"size": len(content),
|
||||
"content_type": content_type,
|
||||
})
|
||||
downloaded.append(
|
||||
{
|
||||
"name": filename,
|
||||
"path": filepath,
|
||||
"size": len(content),
|
||||
"content_type": content_type,
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
errors.append({
|
||||
"name": att.get("name", "unknown"),
|
||||
"error": str(e),
|
||||
})
|
||||
errors.append(
|
||||
{
|
||||
"name": att.get("name", "unknown"),
|
||||
"error": str(e),
|
||||
}
|
||||
)
|
||||
|
||||
result = {
|
||||
"success": len(errors) == 0,
|
||||
@@ -1021,12 +1038,14 @@ def get_email_links(
|
||||
links = extract_links_from_html(body_val)
|
||||
break
|
||||
|
||||
return json.dumps({
|
||||
"item_id": item_id,
|
||||
"subject": subject,
|
||||
"links": links,
|
||||
"count": len(links),
|
||||
})
|
||||
return json.dumps(
|
||||
{
|
||||
"item_id": item_id,
|
||||
"subject": subject,
|
||||
"links": links,
|
||||
"count": len(links),
|
||||
}
|
||||
)
|
||||
|
||||
except SessionExpiredError as e:
|
||||
return json.dumps({"error": str(e)})
|
||||
@@ -1107,16 +1126,22 @@ async def search_emails(
|
||||
|
||||
max_results = max(1, min(max_results, 100))
|
||||
if search_scope not in ("all", "subject", "body", "from"):
|
||||
return json.dumps({"error": f"invalid search_scope: {search_scope}", "results": []})
|
||||
return json.dumps(
|
||||
{"error": f"invalid search_scope: {search_scope}", "results": []}
|
||||
)
|
||||
|
||||
restriction = _build_search_restriction(query.strip(), search_scope)
|
||||
if restriction is None:
|
||||
return json.dumps({"error": f"unsupported search_scope: {search_scope}", "results": []})
|
||||
return json.dumps(
|
||||
{"error": f"unsupported search_scope: {search_scope}", "results": []}
|
||||
)
|
||||
|
||||
if folder_id:
|
||||
parent_folder = [{"__type": "FolderId:#Exchange", "Id": folder_id}]
|
||||
else:
|
||||
parent_folder = [{"__type": "DistinguishedFolderId:#Exchange", "Id": "msgfolderroot"}]
|
||||
parent_folder = [
|
||||
{"__type": "DistinguishedFolderId:#Exchange", "Id": "msgfolderroot"}
|
||||
]
|
||||
|
||||
payload = {
|
||||
"__type": "FindItemJsonRequest:#Exchange",
|
||||
@@ -1153,6 +1178,7 @@ async def search_emails(
|
||||
body_type = item.get("Body", {}).get("BodyType", "HTML")
|
||||
if body_type == "HTML" and body_html:
|
||||
import re
|
||||
|
||||
body_preview = re.sub(r"<[^>]+>", " ", body_html)
|
||||
body_preview = " ".join(body_preview.split())[:200]
|
||||
else:
|
||||
@@ -1163,13 +1189,16 @@ async def search_emails(
|
||||
if len(results) >= max_results:
|
||||
break
|
||||
|
||||
return json.dumps({
|
||||
"query": query.strip(),
|
||||
"search_scope": search_scope,
|
||||
"folder_id": folder_id or "all",
|
||||
"total_results": len(results),
|
||||
"results": results,
|
||||
}, ensure_ascii=False)
|
||||
return json.dumps(
|
||||
{
|
||||
"query": query.strip(),
|
||||
"search_scope": search_scope,
|
||||
"folder_id": folder_id or "all",
|
||||
"total_results": len(results),
|
||||
"results": results,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
except SessionExpiredError as e:
|
||||
return json.dumps({"error": str(e)})
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user