This commit is contained in:
2026-07-10 16:36:22 +03:00
parent 990a8d09b6
commit 17a6f91b3f
7 changed files with 56 additions and 219 deletions
+14 -9
View File
@@ -14,15 +14,17 @@ export function registerAuthTools(server: McpServer): void {
server.registerTool(
"login",
{
description: "Authenticate to Exchange EWS using NTLM credentials",
description: "Authenticate to Exchange EWS using NTLM credentials."
+ " On first use all fields except domain are required."
+ " On subsequent logins only password is needed — previously saved"
+ " serverUrl, email, username, and domain are reused automatically from config."
+ " Returns {success, message} on success or {success, error} on failure.",
inputSchema: z.object({
serverUrl: z.string().optional().describe(
"EWS server URL (e.g. https://mail.example.com)",
),
serverUrl: z.string().optional().describe("Server URL"),
email: z.string().optional().describe("Email address"),
username: z.string().optional().describe("NTLM username"),
password: z.string().describe("NTLM password"),
domain: z.string().optional().describe("NTLM domain (default: corp)"),
domain: z.string().optional().describe("NTLM domain"),
}),
},
async ({ serverUrl, email, username, password, domain }) => {
@@ -47,8 +49,9 @@ export function registerAuthTools(server: McpServer): void {
type: "text" as const,
text: JSON.stringify({
success: false,
error:
"Missing required fields. Provide serverUrl, email, and username, or login once with all fields first.",
error: "Missing required fields."
+ " Provide serverUrl, email, and username, or"
+ " login once with all fields first.",
}),
}],
};
@@ -99,7 +102,8 @@ export function registerAuthTools(server: McpServer): void {
server.registerTool(
"check_session",
{
description: "Check whether the current EWS session is authenticated",
description:
"Check whether the current EWS session is authenticated. No parameters. Returns {authenticated, email, serverUrl} or {authenticated, error}.",
inputSchema: z.object({}),
},
async () => {
@@ -147,7 +151,8 @@ export function registerAuthTools(server: McpServer): void {
server.registerTool(
"logout",
{
description: "Clear stored credentials",
description:
"Clear stored credentials (serverUrl, email, username, domain, password). No parameters. Returns {success, message}.",
inputSchema: z.object({}),
},
async () => {
+12 -10
View File
@@ -6,20 +6,21 @@ export function registerAvailabilityTools(server: McpServer): void {
server.registerTool(
"find_free_time",
{
description: "Find free time slots in your own calendar",
description:
"Find free time slots in your own calendar. Queries your free/busy status and returns available slots within working hours that meet the minimum duration. Returns {freeSlots: {date: [{start, end, durationMinutes}]}}. Defaults: durationMinutes=30, startHour=9, endHour=18.",
inputSchema: z.object({
startDate: z.string().describe("Start date in YYYY-MM-DD format"),
endDate: z.string().optional().describe(
"End date in YYYY-MM-DD format (defaults to startDate)",
"End date in YYYY-MM-DD format. Defaults to startDate",
),
durationMinutes: z.number().default(30).describe(
"Minimum slot duration in minutes (default 30)",
"Minimum slot duration in minutes. Default: 30",
),
startHour: z.number().default(9).describe(
"Working day start hour (0-23, default 9)",
"Working day start hour (0-23). Default: 9",
),
endHour: z.number().default(18).describe(
"Working day end hour (0-23, default 18)",
"Working day end hour (0-23). Default: 18",
),
}),
},
@@ -94,23 +95,24 @@ export function registerAvailabilityTools(server: McpServer): void {
server.registerTool(
"find_meeting_time",
{
description: "Find meeting times that work for multiple people",
description:
"Find common free time for multiple attendees. Queries free/busy for all attendees and returns slots where everyone is available. Returns {period, attendees, freeSlots}. Defaults: durationMinutes=30, startHour=9, endHour=18.",
inputSchema: z.object({
emails: z.string().describe(
"Comma-separated email addresses of attendees",
),
startDate: z.string().describe("Start date in YYYY-MM-DD format"),
endDate: z.string().optional().describe(
"End date in YYYY-MM-DD format (defaults to startDate)",
"End date in YYYY-MM-DD format. Defaults to startDate",
),
durationMinutes: z.number().default(30).describe(
"Minimum slot duration in minutes (default 30)",
"Minimum slot duration in minutes. Default: 30",
),
startHour: z.number().default(9).describe(
"Working day start hour (0-23, default 9)",
"Working day start hour (0-23). Default: 9",
),
endHour: z.number().default(18).describe(
"Working day end hour (0-23, default 18)",
"Working day end hour (0-23). Default: 18",
),
}),
},
+5 -4
View File
@@ -8,12 +8,13 @@ export function registerCalendarTools(server: McpServer): void {
server.registerTool(
"get_calendar_events",
{
description: "Get calendar events within a date range",
description:
"Get calendar events within a date range. Set includeBody=true to fetch organizer, attendees, and body via GetItem. Returns {events, count}.",
inputSchema: z.object({
startDate: z.string().describe("Start date in YYYY-MM-DD format"),
endDate: z.string().describe("End date in YYYY-MM-DD format"),
includeBody: z.boolean().default(false).describe(
"If True, fetch full event details (organizer, attendees, body) via GetItem",
"If true, fetch full event details (organizer, attendees, body) via GetItem. Default: false",
),
}),
},
@@ -84,13 +85,13 @@ export function registerCalendarTools(server: McpServer): void {
"download_event_attachments",
{
description:
"Download all file attachments from a calendar event to disk",
"Download all file attachments from a calendar event to disk. Inline (embedded) attachments are skipped — only standalone file attachments are downloaded. Returns {success, downloaded, count} or {success, downloaded, count, errors}.",
inputSchema: z.object({
itemId: z.string().describe(
"The Exchange ItemId of the calendar event",
),
targetFolder: z.string().default("/tmp/attachments").describe(
"Local directory to save files (default /tmp/attachments)",
"Local directory to save files. Default: /tmp/attachments",
),
}),
},
+19 -13
View File
@@ -12,31 +12,34 @@ export function registerEmailTools(server: McpServer): void {
server.registerTool(
"get_emails",
{
description: "Get emails from a mailbox folder",
description:
"Get emails from a mailbox folder. Supports optional text search via query/queryScope. Returns {emails, count} or {itemIds, count} if idsOnly. Defaults: folder=Inbox, limit=10, offset=0, includeBody=false, unreadOnly=false, idsOnly=false, queryScope=all. Note: idsOnly raises the limit to 500.",
inputSchema: z.object({
folder: z.string().default("Inbox").describe(
"Folder name (Inbox, Sent, Drafts, Deleted, Junk, or custom)",
"Folder name (Inbox, Sent, Drafts, Deleted, Junk, or custom). Supports Russian folder names",
),
limit: z.number().default(10).describe(
"Maximum number of emails to return (default 10, max 50)",
"Maximum number of emails to return. Default: 10. Max 50 (max 500 if idsOnly=true)",
),
offset: z.number().default(0).describe(
"Number of emails to skip for pagination",
"Number of emails to skip for pagination. Default: 0",
),
includeBody: z.boolean().default(false).describe(
"If True, fetch full body for each email (slower)",
"If true, fetches full email body for each email (slower). Default: false",
),
unreadOnly: z.boolean().default(false).describe(
"If True, only return unread emails",
"If true, only return unread emails. Default: false",
),
idsOnly: z.boolean().default(false).describe(
"If True, return only item IDs and dates (max limit 500)",
"If true, return only item IDs, dates, and subjects — faster with higher limit of 500. Default: false",
),
query: z.string().optional().describe(
"Optional text to search for within the folder",
),
queryScope: z.enum(["all", "subject", "body", "from"]).default("all")
.describe("Where to search (only used when query is set)"),
.describe(
"Scope for text search. Values: all (subject+body), subject, body, from. Only used when query is set. Default: all",
),
}),
},
async (
@@ -177,7 +180,8 @@ export function registerEmailTools(server: McpServer): void {
server.registerTool(
"get_email",
{
description: "Get a single email with full body and details",
description:
"Get a single email with full body and details by Exchange ItemId. Returns the full Email object (subject, from, to, cc, body, date, attachments, etc.).",
inputSchema: z.object({
itemId: z.string().describe(
"The Exchange ItemId of the email to retrieve",
@@ -207,13 +211,14 @@ export function registerEmailTools(server: McpServer): void {
server.registerTool(
"mark_email_read",
{
description: "Mark one or more emails as read or unread",
description:
"Mark one or more emails as read or unread by their Exchange ItemIds. Returns {success, message} or {error}.",
inputSchema: z.object({
itemIds: z.array(z.string()).describe(
"List of Exchange ItemIds to update",
),
isRead: z.boolean().default(true).describe(
"True to mark as read, False to mark as unread",
"True to mark as read, false to mark as unread. Default: true",
),
}),
},
@@ -261,11 +266,12 @@ export function registerEmailTools(server: McpServer): void {
server.registerTool(
"download_attachments",
{
description: "Download all file attachments from an email to disk",
description:
"Download all file attachments from an email to disk. Inline (embedded) attachments are skipped — only standalone file attachments are downloaded. Returns {success, downloaded, count} or {success, downloaded, count, errors}.",
inputSchema: z.object({
itemId: z.string().describe("The Exchange ItemId of the email"),
targetFolder: z.string().default("/tmp/attachments").describe(
"Local directory to save files (default /tmp/attachments)",
"Local directory to save files. Default: /tmp/attachments",
),
}),
},
+4 -3
View File
@@ -6,13 +6,14 @@ export function registerFolderTools(server: McpServer): void {
server.registerTool(
"get_folders",
{
description: "List mail folders from the Exchange mailbox",
description:
"List mailbox folders from the Exchange mailbox. Returns a list of Folder objects (name, id, totalCount, unreadCount, childFolderCount).",
inputSchema: z.object({
parentFolderId: z.string().default("msgfolderroot").describe(
"Parent folder to list children of (default: msgfolderroot)",
"Parent folder to list children of. Supports distinguished names (e.g. inbox, calendar). Default: msgfolderroot",
),
recursive: z.boolean().default(false).describe(
"If True, traverse all subfolders recursively",
"If true, recursively traverse all subfolders. Default: false",
),
}),
},
+2 -1
View File
@@ -6,7 +6,8 @@ export function registerPeopleTools(server: McpServer): void {
server.registerTool(
"find_person",
{
description: "Search for people in the corporate directory",
description:
"Search for people in the corporate directory (Active Directory) by name, email, or keyword. Returns a list of Person objects with name, email, jobTitle, department, company, office, phones, manager, directReports, and more.",
inputSchema: z.object({
query: z.string().describe(
"Name, email address, or keyword to search for",