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"] ?? "";