From 8fce57800fbf543abca5a45de3d7d614b08ce033 Mon Sep 17 00:00:00 2001 From: albnnc Date: Thu, 9 Jul 2026 16:20:46 +0300 Subject: [PATCH] w --- SKILL.md | 13 ++++---- ews_client.ts | 10 +++++- tools/email.ts | 90 +++++++++++++++++--------------------------------- 3 files changed, 46 insertions(+), 67 deletions(-) diff --git a/SKILL.md b/SKILL.md index 3ab6aaa..e6d15f1 100644 --- a/SKILL.md +++ b/SKILL.md @@ -79,12 +79,13 @@ Get a single email with full body and details. Search emails by text. -| Parameter | Type | Default | Description | -| ------------- | ------ | -------- | --------------------------------------- | -| `query` | string | — | Text to search for | -| `folderId` | string | optional | Folder ID to limit search | -| `maxResults` | number | `20` | Max results (max 100) | -| `searchScope` | enum | `"all"` | Scope: `all`, `subject`, `body`, `from` | +| Parameter | Type | Default | Description | +| ------------- | ------ | -------- | ------------------------------------------- | +| `query` | string | — | Text to search for | +| `folderId` | string | optional | Folder ID to limit search; omitted = all | +| `limit` | number | `10` | Max emails (max 50) | +| `offset` | number | `0` | Pagination offset | +| `searchScope` | enum | `"all"` | Scope: `all`, `subject`, `body`, `from` | #### `mark_email_read` diff --git a/ews_client.ts b/ews_client.ts index 8c2f983..41634c9 100644 --- a/ews_client.ts +++ b/ews_client.ts @@ -250,6 +250,14 @@ export class EwsClient { traversal = "Shallow", } = options; + const isDistinguished = + DISTINGUISHED_FOLDERS[folderId.toLowerCase()] !== undefined + || ["msgfolderroot"].includes(folderId.toLowerCase()); + + const folderIdXml = isDistinguished + ? `` + : ``; + const soap = buildSoapEnvelope(`\ @@ -257,7 +265,7 @@ export class EwsClient { - + ${folderIdXml} diff --git a/tools/email.ts b/tools/email.ts index 469aa5d..6c82f93 100644 --- a/tools/email.ts +++ b/tools/email.ts @@ -148,14 +148,17 @@ export function registerEmailTools(server: McpServer): void { folderId: z.string().optional().describe( "Optional folder ID to limit the search. When omitted, searches all mail folders", ), - maxResults: z.number().default(20).describe( - "Maximum number of results (default 20, max 100)", + limit: z.number().default(10).describe( + "Maximum number of emails to return (default 10, max 50)", + ), + offset: z.number().default(0).describe( + "Number of emails to skip for pagination", ), searchScope: z.enum(["all", "subject", "body", "from"]).default("all") .describe("Where to search"), }), }, - async ({ query, folderId, maxResults, searchScope }) => { + async ({ query, folderId, limit, offset, searchScope }) => { try { const client = getClient(); @@ -171,7 +174,8 @@ export function registerEmailTools(server: McpServer): void { }; } - maxResults = Math.max(1, Math.min(maxResults, 100)); + limit = Math.max(1, Math.min(limit, 50)); + offset = Math.max(0, offset); const fieldUriMap: Record = { subject: "item:Subject", @@ -220,63 +224,31 @@ export function registerEmailTools(server: McpServer): void { `; } + const targetFolder = folderId || "msgfolderroot"; const traversal = folderId ? "Shallow" : "Deep"; - if (folderId) { - const items = await client.findItems(folderId, { - limit: maxResults, - restriction, - traversal, - }); - const results = formatSearchResults(items, client, maxResults); + const items = await client.findItems(targetFolder, { + limit, + offset, + restriction, + traversal, + }); + const results = formatSearchResults(items, client); - return { - content: [{ - type: "text" as const, - text: JSON.stringify({ - query, - searchScope, - folderId, - totalResults: results.length, - results, - }), - }], - }; - } else { - const folders = await client.findFolders("msgfolderroot", true); - const allResults = []; - - for (const f of folders) { - if (allResults.length >= maxResults) break; - const remaining = maxResults - allResults.length; - const items = await client.findItems(f.id, { - limit: remaining, - restriction, - traversal: "Shallow", - }); - const formatted = formatSearchResults(items, client, remaining); - - for (const r of formatted) { - r.folderId = f.id; - r.folderName = f.name; - allResults.push(r); - if (allResults.length >= maxResults) break; - } - } - - return { - content: [{ - type: "text" as const, - text: JSON.stringify({ - query, - searchScope, - folderId: "all", - totalResults: allResults.length, - results: allResults, - }), - }], - }; - } + return { + content: [{ + type: "text" as const, + text: JSON.stringify({ + query, + searchScope, + folderId: folderId || "all", + limit, + offset, + totalResults: results.length, + results, + }), + }], + }; } catch (error: any) { return { content: [{ @@ -448,11 +420,9 @@ export function registerEmailTools(server: McpServer): void { function formatSearchResults( items: any[], client: EwsClient, - maxResults: number, ): any[] { const results: any[] = []; for (const item of items) { - if (results.length >= maxResults) break; const summary = client.extractEmailSummary(item); const bodyHtml = item.Body?.Value ?? item.Body?.["#text"] ?? "";